diff options
Diffstat (limited to 'src/core/hle')
577 files changed, 24732 insertions, 9510 deletions
diff --git a/src/core/hle/api_version.h b/src/core/hle/api_version.h index 626e30753..bd15606e1 100644 --- a/src/core/hle/api_version.h +++ b/src/core/hle/api_version.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 602e12606..416da15ec 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h @@ -1,6 +1,5 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2016 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 026257115..d631c0357 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -1,6 +1,5 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2016 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -19,7 +18,7 @@ namespace IPC { -constexpr ResultCode ERR_REMOTE_PROCESS_DEAD{ErrorModule::HIPC, 301}; +constexpr Result ERR_REMOTE_PROCESS_DEAD{ErrorModule::HIPC, 301}; class RequestHelperBase { protected: @@ -176,7 +175,7 @@ public: void PushImpl(float value); void PushImpl(double value); void PushImpl(bool value); - void PushImpl(ResultCode value); + void PushImpl(Result value); template <typename T> void Push(T value) { @@ -251,7 +250,7 @@ void ResponseBuilder::PushRaw(const T& value) { index += (sizeof(T) + 3) / 4; // round up to word length } -inline void ResponseBuilder::PushImpl(ResultCode value) { +inline void ResponseBuilder::PushImpl(Result value) { // Result codes are actually 64-bit in the IPC buffer, but only the high part is discarded. Push(value.raw); Push<u32>(0); @@ -385,7 +384,7 @@ public: T PopRaw(); template <class T> - std::shared_ptr<T> PopIpcInterface() { + std::weak_ptr<T> PopIpcInterface() { ASSERT(context->Session()->IsDomain()); ASSERT(context->GetDomainMessageHeader().input_object_count > 0); return context->GetDomainHandler<T>(Pop<u32>() - 1); @@ -481,8 +480,8 @@ inline bool RequestParser::Pop() { } template <> -inline ResultCode RequestParser::Pop() { - return ResultCode{Pop<u32>()}; +inline Result RequestParser::Pop() { + return Result{Pop<u32>()}; } template <typename T> diff --git a/src/core/hle/kernel/arch/arm64/k_memory_region_device_types.inc b/src/core/hle/kernel/arch/arm64/k_memory_region_device_types.inc index 857b512ba..2a0f2d5f7 100644 --- a/src/core/hle/kernel/arch/arm64/k_memory_region_device_types.inc +++ b/src/core/hle/kernel/arch/arm64/k_memory_region_device_types.inc @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later // All architectures must define NumArchitectureDeviceRegions. constexpr inline const auto NumArchitectureDeviceRegions = 3; diff --git a/src/core/hle/kernel/board/nintendo/nx/k_memory_layout.h b/src/core/hle/kernel/board/nintendo/nx/k_memory_layout.h new file mode 100644 index 000000000..d02ee61c3 --- /dev/null +++ b/src/core/hle/kernel/board/nintendo/nx/k_memory_layout.h @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Kernel { + +constexpr inline PAddr MainMemoryAddress = 0x80000000; + +} // namespace Kernel diff --git a/src/core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc b/src/core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc index 58d6c0b16..5f5ec6d5b 100644 --- a/src/core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc +++ b/src/core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later // All architectures must define NumBoardDeviceRegions. constexpr inline const auto NumBoardDeviceRegions = 6; diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp index 6f335c251..c10b7bf30 100644 --- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp +++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp @@ -1,10 +1,10 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <random> #include "common/literals.h" +#include "common/settings.h" #include "core/hle/kernel/board/nintendo/nx/k_system_control.h" #include "core/hle/kernel/board/nintendo/nx/secure_monitor.h" @@ -28,33 +28,20 @@ namespace { using namespace Common::Literals; -u32 GetMemoryModeForInit() { - return 0x01; -} - u32 GetMemorySizeForInit() { - return 0; + return Settings::values.use_extended_memory_layout ? Smc::MemorySize_6GB : Smc::MemorySize_4GB; } Smc::MemoryArrangement GetMemoryArrangeForInit() { - switch (GetMemoryModeForInit() & 0x3F) { - case 0x01: - default: - return Smc::MemoryArrangement_4GB; - case 0x02: - return Smc::MemoryArrangement_4GBForAppletDev; - case 0x03: - return Smc::MemoryArrangement_4GBForSystemDev; - case 0x11: - return Smc::MemoryArrangement_6GB; - case 0x12: - return Smc::MemoryArrangement_6GBForAppletDev; - case 0x21: - return Smc::MemoryArrangement_8GB; - } + return Settings::values.use_extended_memory_layout ? Smc::MemoryArrangement_6GB + : Smc::MemoryArrangement_4GB; } } // namespace +size_t KSystemControl::Init::GetRealMemorySize() { + return GetIntendedMemorySize(); +} + // Initialization. size_t KSystemControl::Init::GetIntendedMemorySize() { switch (GetMemorySizeForInit()) { @@ -69,7 +56,13 @@ size_t KSystemControl::Init::GetIntendedMemorySize() { } PAddr KSystemControl::Init::GetKernelPhysicalBaseAddress(u64 base_address) { - return base_address; + const size_t real_dram_size = KSystemControl::Init::GetRealMemorySize(); + const size_t intended_dram_size = KSystemControl::Init::GetIntendedMemorySize(); + if (intended_dram_size * 2 < real_dram_size) { + return base_address; + } else { + return base_address + ((real_dram_size - intended_dram_size) / 2); + } } bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() { @@ -154,9 +147,9 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) { } // Anonymous namespace u64 KSystemControl::GenerateRandomU64() { - static std::random_device device; - static std::mt19937 gen(device()); - static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); + std::random_device device; + std::mt19937 gen(device()); + std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); return distribution(gen); } diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h index 52f230ced..fe375769e 100644 --- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h +++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -13,6 +12,7 @@ public: class Init { public: // Initialization. + static std::size_t GetRealMemorySize(); static std::size_t GetIntendedMemorySize(); static PAddr GetKernelPhysicalBaseAddress(u64 base_address); static bool ShouldIncreaseThreadResourceLimit(); diff --git a/src/core/hle/kernel/board/nintendo/nx/secure_monitor.h b/src/core/hle/kernel/board/nintendo/nx/secure_monitor.h index f77a91dec..b0e4123f0 100644 --- a/src/core/hle/kernel/board/nintendo/nx/secure_monitor.h +++ b/src/core/hle/kernel/board/nintendo/nx/secure_monitor.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/code_set.cpp b/src/core/hle/kernel/code_set.cpp index 1f434e9af..41386048b 100644 --- a/src/core/hle/kernel/code_set.cpp +++ b/src/core/hle/kernel/code_set.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/code_set.h" diff --git a/src/core/hle/kernel/code_set.h b/src/core/hle/kernel/code_set.h index 5cc3b9829..5220dbcb6 100644 --- a/src/core/hle/kernel/code_set.h +++ b/src/core/hle/kernel/code_set.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp index baad2c5d6..65576b8c4 100644 --- a/src/core/hle/kernel/global_scheduler_context.cpp +++ b/src/core/hle/kernel/global_scheduler_context.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <mutex> @@ -42,12 +41,7 @@ void GlobalSchedulerContext::PreemptThreads() { ASSERT(IsLocked()); for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { const u32 priority = preemption_priorities[core_id]; - kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority); - - // Signal an interrupt occurred. For core 3, this is a certainty, as preemption will result - // in the rotator thread being scheduled. For cores 0-2, this is to simulate or system - // interrupts that may have occurred. - kernel.PhysicalCore(core_id).Interrupt(); + KScheduler::RotateScheduledQueue(kernel, core_id, priority); } } diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h index 6f44b534f..67bb9852d 100644 --- a/src/core/hle/kernel/global_scheduler_context.h +++ b/src/core/hle/kernel/global_scheduler_context.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,7 +7,6 @@ #include <vector> #include "common/common_types.h" -#include "common/spin_lock.h" #include "core/hardware_properties.h" #include "core/hle/kernel/k_priority_queue.h" #include "core/hle/kernel/k_scheduler_lock.h" @@ -80,7 +78,7 @@ private: /// Lists all thread ids that aren't deleted/etc. std::vector<KThread*> thread_list; - Common::SpinLock global_list_guard{}; + std::mutex global_list_guard; }; } // namespace Kernel diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index e19544c54..5b3feec66 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> @@ -17,7 +16,6 @@ #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_process.h" -#include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" @@ -25,8 +23,15 @@ namespace Kernel { -SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_) - : kernel{kernel_}, service_thread{kernel.CreateServiceThread(service_name_)} {} +SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_, + ServiceThreadType thread_type) + : kernel{kernel_} { + if (thread_type == ServiceThreadType::CreateNew) { + service_thread = kernel.CreateServiceThread(service_name_); + } else { + service_thread = kernel.GetDefaultServiceThread(); + } +} SessionRequestHandler::~SessionRequestHandler() { kernel.ReleaseServiceThread(service_thread); @@ -45,7 +50,7 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co LOG_CRITICAL(IPC, "object_id {} is too big!", object_id); return false; } - return DomainHandler(object_id - 1) != nullptr; + return !DomainHandler(object_id - 1).expired(); } else { return session_handler != nullptr; } @@ -55,7 +60,7 @@ void SessionRequestHandler::ClientConnected(KServerSession* session) { session->ClientConnected(shared_from_this()); // Ensure our server session is tracked globally. - kernel.RegisterServerSession(session); + kernel.RegisterServerObject(session); } void SessionRequestHandler::ClientDisconnected(KServerSession* session) { @@ -183,8 +188,8 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 rp.Skip(1, false); // The command is actually an u64, but we don't use the high part. } -ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table, - u32_le* src_cmdbuf) { +Result HLERequestContext::PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table, + u32_le* src_cmdbuf) { ParseCommandBuffer(handle_table, src_cmdbuf, true); if (command_header->IsCloseCommand()) { @@ -197,7 +202,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const KHandleTab return ResultSuccess; } -ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_thread) { +Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_thread) { auto current_offset = handles_offset; auto& owner_process = *requesting_thread.GetOwnerProcess(); auto& handle_table = owner_process.GetHandleTable(); @@ -282,15 +287,49 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, BufferDescriptorB().size() > buffer_index && BufferDescriptorB()[buffer_index].Size() >= size, { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size); - memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); + WriteBufferB(buffer, size, buffer_index); } else { ASSERT_OR_EXECUTE_MSG( BufferDescriptorC().size() > buffer_index && BufferDescriptorC()[buffer_index].Size() >= size, { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size); - memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); + WriteBufferC(buffer, size, buffer_index); + } + + return size; +} + +std::size_t HLERequestContext::WriteBufferB(const void* buffer, std::size_t size, + std::size_t buffer_index) const { + if (buffer_index >= BufferDescriptorB().size() || size == 0) { + return 0; + } + + const auto buffer_size{BufferDescriptorB()[buffer_index].Size()}; + if (size > buffer_size) { + LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, + buffer_size); + size = buffer_size; // TODO(bunnei): This needs to be HW tested + } + + memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); + return size; +} + +std::size_t HLERequestContext::WriteBufferC(const void* buffer, std::size_t size, + std::size_t buffer_index) const { + if (buffer_index >= BufferDescriptorC().size() || size == 0) { + return 0; + } + + const auto buffer_size{BufferDescriptorC()[buffer_index].Size()}; + if (size > buffer_size) { + LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, + buffer_size); + size = buffer_size; // TODO(bunnei): This needs to be HW tested } + memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); return size; } diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 754b41ff6..99265ce90 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -19,7 +18,7 @@ #include "core/hle/ipc.h" #include "core/hle/kernel/svc_common.h" -union ResultCode; +union Result; namespace Core::Memory { class Memory; @@ -33,6 +32,11 @@ namespace Service { class ServiceFrameworkBase; } +enum class ServiceThreadType { + Default, + CreateNew, +}; + namespace Kernel { class Domain; @@ -57,7 +61,8 @@ enum class ThreadWakeupReason; */ class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> { public: - SessionRequestHandler(KernelCore& kernel, const char* service_name_); + SessionRequestHandler(KernelCore& kernel_, const char* service_name_, + ServiceThreadType thread_type); virtual ~SessionRequestHandler(); /** @@ -66,10 +71,10 @@ public: * it should be used to differentiate which client (As in ClientSession) we're answering to. * TODO(Subv): Use a wrapper structure to hold all the information relevant to * this request (ServerSession, Originator thread, Translated command buffer, etc). - * @returns ResultCode the result code of the translate operation. + * @returns Result the result code of the translate operation. */ - virtual ResultCode HandleSyncRequest(Kernel::KServerSession& session, - Kernel::HLERequestContext& context) = 0; + virtual Result HandleSyncRequest(Kernel::KServerSession& session, + Kernel::HLERequestContext& context) = 0; /** * Signals that a client has just connected to this HLE handler and keeps the @@ -94,6 +99,7 @@ protected: std::weak_ptr<ServiceThread> service_thread; }; +using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>; using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>; /** @@ -135,11 +141,11 @@ public: if (index < DomainHandlerCount()) { domain_handlers[index] = nullptr; } else { - UNREACHABLE_MSG("Unexpected handler index {}", index); + ASSERT_MSG(false, "Unexpected handler index {}", index); } } - SessionRequestHandlerPtr DomainHandler(std::size_t index) const { + SessionRequestHandlerWeakPtr DomainHandler(std::size_t index) const { ASSERT_MSG(index < DomainHandlerCount(), "Unexpected handler index {}", index); return domain_handlers.at(index); } @@ -206,11 +212,10 @@ public: } /// Populates this context with data from the requesting process/thread. - ResultCode PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table, - u32_le* src_cmdbuf); + Result PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table, u32_le* src_cmdbuf); /// Writes data from this context back to the requesting process/thread. - ResultCode WriteToOutgoingCommandBuffer(KThread& requesting_thread); + Result WriteToOutgoingCommandBuffer(KThread& requesting_thread); u32_le GetHipcCommand() const { return command; @@ -272,6 +277,14 @@ public: std::size_t WriteBuffer(const void* buffer, std::size_t size, std::size_t buffer_index = 0) const; + /// Helper function to write buffer B + std::size_t WriteBufferB(const void* buffer, std::size_t size, + std::size_t buffer_index = 0) const; + + /// Helper function to write buffer C + std::size_t WriteBufferC(const void* buffer, std::size_t size, + std::size_t buffer_index = 0) const; + /* Helper function to write a buffer using the appropriate buffer descriptor * * @tparam T an arbitrary container that satisfies the @@ -328,10 +341,10 @@ public: template <typename T> std::shared_ptr<T> GetDomainHandler(std::size_t index) const { - return std::static_pointer_cast<T>(manager->DomainHandler(index)); + return std::static_pointer_cast<T>(manager.lock()->DomainHandler(index).lock()); } - void SetSessionRequestManager(std::shared_ptr<SessionRequestManager> manager_) { + void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) { manager = std::move(manager_); } @@ -374,7 +387,7 @@ private: u32 handles_offset{}; u32 domain_offset{}; - std::shared_ptr<SessionRequestManager> manager; + std::weak_ptr<SessionRequestManager> manager; KernelCore& kernel; Core::Memory::Memory& memory; diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index 36fc0944a..9b6b284d0 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp @@ -1,25 +1,28 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/alignment.h" #include "common/assert.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "core/core.h" +#include "core/device_memory.h" #include "core/hardware_properties.h" #include "core/hle/kernel/init/init_slab_setup.h" #include "core/hle/kernel/k_code_memory.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_manager.h" +#include "core/hle/kernel/k_page_buffer.h" #include "core/hle/kernel/k_port.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_session.h" #include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/k_shared_memory_info.h" #include "core/hle/kernel/k_system_control.h" #include "core/hle/kernel/k_thread.h" +#include "core/hle/kernel/k_thread_local_page.h" #include "core/hle/kernel/k_transfer_memory.h" namespace Kernel::Init { @@ -32,9 +35,13 @@ namespace Kernel::Init { HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \ HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ + HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \ HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \ HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \ + HANDLER(KThreadLocalPage, \ + (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \ + ##__VA_ARGS__) \ HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) namespace { @@ -50,38 +57,46 @@ enum KSlabType : u32 { // Constexpr counts. constexpr size_t SlabCountKProcess = 80; constexpr size_t SlabCountKThread = 800; -constexpr size_t SlabCountKEvent = 700; +constexpr size_t SlabCountKEvent = 900; constexpr size_t SlabCountKInterruptEvent = 100; -constexpr size_t SlabCountKPort = 256 + 0x20; // Extra 0x20 ports over Nintendo for homebrew. +constexpr size_t SlabCountKPort = 384; constexpr size_t SlabCountKSharedMemory = 80; constexpr size_t SlabCountKTransferMemory = 200; constexpr size_t SlabCountKCodeMemory = 10; constexpr size_t SlabCountKDeviceAddressSpace = 300; -constexpr size_t SlabCountKSession = 933; +constexpr size_t SlabCountKSession = 1133; constexpr size_t SlabCountKLightSession = 100; constexpr size_t SlabCountKObjectName = 7; constexpr size_t SlabCountKResourceLimit = 5; constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES; -constexpr size_t SlabCountKAlpha = 1; -constexpr size_t SlabCountKBeta = 6; +constexpr size_t SlabCountKIoPool = 1; +constexpr size_t SlabCountKIoRegion = 6; constexpr size_t SlabCountExtraKThread = 160; +/// Helper function to translate from the slab virtual address to the reserved location in physical +/// memory. +static PAddr TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout, VAddr slab_addr) { + slab_addr -= memory_layout.GetSlabRegionAddress(); + return slab_addr + Core::DramMemoryMap::SlabHeapBase; +} + template <typename T> VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAddr address, size_t num_objects) { - // TODO(bunnei): This is just a place holder. We should initialize the appropriate KSlabHeap for - // kernel object type T with the backing kernel memory pointer once we emulate kernel memory. const size_t size = Common::AlignUp(sizeof(T) * num_objects, alignof(void*)); VAddr start = Common::AlignUp(address, alignof(T)); - // This is intentionally empty. Once KSlabHeap is fully implemented, we can replace this with - // the pointer to emulated memory to pass along. Until then, KSlabHeap will just allocate/free - // host memory. - void* backing_kernel_memory{}; + // This should use the virtual memory address passed in, but currently, we do not setup the + // kernel virtual memory layout. Instead, we simply map these at a region of physical memory + // that we reserve for the slab heaps. + // TODO(bunnei): Fix this once we support the kernel virtual memory layout. if (size > 0) { + void* backing_kernel_memory{ + system.DeviceMemory().GetPointer(TranslateSlabAddrToPhysical(memory_layout, start))}; + const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1); ASSERT(region != nullptr); ASSERT(region->IsDerivedFrom(KMemoryRegionType_KernelSlab)); @@ -91,6 +106,12 @@ VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAd return start + size; } +size_t CalculateSlabHeapGapSize() { + constexpr size_t KernelSlabHeapGapSize = 2_MiB - 296_KiB; + static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax); + return KernelSlabHeapGapSize; +} + } // namespace KSlabResourceCounts KSlabResourceCounts::CreateDefault() { @@ -109,8 +130,8 @@ KSlabResourceCounts KSlabResourceCounts::CreateDefault() { .num_KObjectName = SlabCountKObjectName, .num_KResourceLimit = SlabCountKResourceLimit, .num_KDebug = SlabCountKDebug, - .num_KAlpha = SlabCountKAlpha, - .num_KBeta = SlabCountKBeta, + .num_KIoPool = SlabCountKIoPool, + .num_KIoRegion = SlabCountKIoRegion, }; } @@ -136,11 +157,34 @@ size_t CalculateTotalSlabHeapSize(const KernelCore& kernel) { #undef ADD_SLAB_SIZE // Add the reserved size. - size += KernelSlabHeapGapsSize; + size += CalculateSlabHeapGapSize(); return size; } +void InitializeKPageBufferSlabHeap(Core::System& system) { + auto& kernel = system.Kernel(); + + const auto& counts = kernel.SlabResourceCounts(); + const size_t num_pages = + counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8; + const size_t slab_size = num_pages * PageSize; + + // Reserve memory from the system resource limit. + ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size)); + + // Allocate memory for the slab. + constexpr auto AllocateOption = KMemoryManager::EncodeOption( + KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront); + const PAddr slab_address = + kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption); + ASSERT(slab_address != 0); + + // Initialize the slabheap. + KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer(slab_address), + slab_size); +} + void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { auto& kernel = system.Kernel(); @@ -160,13 +204,13 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { } // Create an array to represent the gaps between the slabs. - const size_t total_gap_size = KernelSlabHeapGapsSize; + const size_t total_gap_size = CalculateSlabHeapGapSize(); std::array<size_t, slab_types.size()> slab_gaps; - for (size_t i = 0; i < slab_gaps.size(); i++) { + for (auto& slab_gap : slab_gaps) { // Note: This is an off-by-one error from Nintendo's intention, because GenerateRandomRange // is inclusive. However, Nintendo also has the off-by-one error, and it's "harmless", so we // will include it ourselves. - slab_gaps[i] = KSystemControl::GenerateRandomRange(0, total_gap_size); + slab_gap = KSystemControl::GenerateRandomRange(0, total_gap_size); } // Sort the array, so that we can treat differences between values as offsets to the starts of @@ -177,13 +221,21 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { } } - for (size_t i = 0; i < slab_types.size(); i++) { + // Track the gaps, so that we can free them to the unused slab tree. + VAddr gap_start = address; + size_t gap_size = 0; + + for (size_t i = 0; i < slab_gaps.size(); i++) { // Add the random gap to the address. - address += (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1]; + const auto cur_gap = (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1]; + address += cur_gap; + gap_size += cur_gap; #define INITIALIZE_SLAB_HEAP(NAME, COUNT, ...) \ case KSlabType_##NAME: \ - address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \ + if (COUNT > 0) { \ + address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \ + } \ break; // Initialize the slabheap. @@ -192,7 +244,13 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP) // If we somehow get an invalid type, abort. default: - UNREACHABLE(); + ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]); + } + + // If we've hit the end of a gap, free it. + if (gap_start + gap_size != address) { + gap_start = address; + gap_size = 0; } } } diff --git a/src/core/hle/kernel/init/init_slab_setup.h b/src/core/hle/kernel/init/init_slab_setup.h index a8f7e0918..13be63c87 100644 --- a/src/core/hle/kernel/init/init_slab_setup.h +++ b/src/core/hle/kernel/init/init_slab_setup.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -32,12 +31,13 @@ struct KSlabResourceCounts { size_t num_KObjectName; size_t num_KResourceLimit; size_t num_KDebug; - size_t num_KAlpha; - size_t num_KBeta; + size_t num_KIoPool; + size_t num_KIoRegion; }; void InitializeSlabResourceCounts(KernelCore& kernel); size_t CalculateTotalSlabHeapSize(const KernelCore& kernel); +void InitializeKPageBufferSlabHeap(Core::System& system); void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout); } // namespace Kernel::Init diff --git a/src/core/hle/kernel/initial_process.h b/src/core/hle/kernel/initial_process.h new file mode 100644 index 000000000..af0fb23b6 --- /dev/null +++ b/src/core/hle/kernel/initial_process.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "common/literals.h" +#include "core/hle/kernel/board/nintendo/nx/k_memory_layout.h" +#include "core/hle/kernel/board/nintendo/nx/k_system_control.h" + +namespace Kernel { + +using namespace Common::Literals; + +constexpr std::size_t InitialProcessBinarySizeMax = 12_MiB; + +static inline PAddr GetInitialProcessBinaryPhysicalAddress() { + return Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetKernelPhysicalBaseAddress( + MainMemoryAddress); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp index 783c69858..f85b11557 100644 --- a/src/core/hle/kernel/k_address_arbiter.cpp +++ b/src/core/hle/kernel/k_address_arbiter.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/arm/exclusive_monitor.h" #include "core/core.h" @@ -49,7 +48,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu } } else { // Otherwise, clear our exclusive hold and finish - monitor.ClearExclusive(); + monitor.ClearExclusive(current_core); } // We're done. @@ -78,7 +77,7 @@ bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 } } else { // Otherwise, clear our exclusive hold and finish. - monitor.ClearExclusive(); + monitor.ClearExclusive(current_core); } // We're done. @@ -91,8 +90,7 @@ public: explicit ThreadQueueImplForKAddressArbiter(KernelCore& kernel_, KAddressArbiter::ThreadTree* t) : KThreadQueue(kernel_), m_tree(t) {} - void CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task) override { + void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // If the thread is waiting on an address arbiter, remove it from the tree. if (waiting_thread->IsWaitingForAddressArbiter()) { m_tree->erase(m_tree->iterator_to(*waiting_thread)); @@ -109,13 +107,13 @@ private: } // namespace -ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) { +Result KAddressArbiter::Signal(VAddr addr, s32 count) { // Perform signaling. s32 num_waiters{}; { KScopedSchedulerLock sl(kernel); - auto it = thread_tree.nfind_light({addr, -1}); + auto it = thread_tree.nfind_key({addr, -1}); while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetAddressArbiterKey() == addr)) { // End the thread's wait. @@ -132,7 +130,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) { return ResultSuccess; } -ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) { +Result KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) { // Perform signaling. s32 num_waiters{}; { @@ -148,7 +146,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 return ResultInvalidState; } - auto it = thread_tree.nfind_light({addr, -1}); + auto it = thread_tree.nfind_key({addr, -1}); while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetAddressArbiterKey() == addr)) { // End the thread's wait. @@ -165,13 +163,13 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 return ResultSuccess; } -ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) { +Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) { // Perform signaling. s32 num_waiters{}; { [[maybe_unused]] const KScopedSchedulerLock sl(kernel); - auto it = thread_tree.nfind_light({addr, -1}); + auto it = thread_tree.nfind_key({addr, -1}); // Determine the updated value. s32 new_value{}; if (count <= 0) { @@ -233,9 +231,9 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 return ResultSuccess; } -ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) { +Result KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) { // Prepare to wait. - KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); + KThread* cur_thread = GetCurrentThreadPointer(kernel); ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree)); { @@ -286,9 +284,9 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement return cur_thread->GetWaitResult(); } -ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { +Result KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { // Prepare to wait. - KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); + KThread* cur_thread = GetCurrentThreadPointer(kernel); ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree)); { diff --git a/src/core/hle/kernel/k_address_arbiter.h b/src/core/hle/kernel/k_address_arbiter.h index bf8b46665..e4085ae22 100644 --- a/src/core/hle/kernel/k_address_arbiter.h +++ b/src/core/hle/kernel/k_address_arbiter.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -9,7 +8,7 @@ #include "core/hle/kernel/k_condition_variable.h" #include "core/hle/kernel/svc_types.h" -union ResultCode; +union Result; namespace Core { class System; @@ -26,8 +25,7 @@ public: explicit KAddressArbiter(Core::System& system_); ~KAddressArbiter(); - [[nodiscard]] ResultCode SignalToAddress(VAddr addr, Svc::SignalType type, s32 value, - s32 count) { + [[nodiscard]] Result SignalToAddress(VAddr addr, Svc::SignalType type, s32 value, s32 count) { switch (type) { case Svc::SignalType::Signal: return Signal(addr, count); @@ -36,12 +34,12 @@ public: case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual: return SignalAndModifyByWaitingCountIfEqual(addr, value, count); } - UNREACHABLE(); + ASSERT(false); return ResultUnknown; } - [[nodiscard]] ResultCode WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value, - s64 timeout) { + [[nodiscard]] Result WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value, + s64 timeout) { switch (type) { case Svc::ArbitrationType::WaitIfLessThan: return WaitIfLessThan(addr, value, false, timeout); @@ -50,16 +48,16 @@ public: case Svc::ArbitrationType::WaitIfEqual: return WaitIfEqual(addr, value, timeout); } - UNREACHABLE(); + ASSERT(false); return ResultUnknown; } private: - [[nodiscard]] ResultCode Signal(VAddr addr, s32 count); - [[nodiscard]] ResultCode SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count); - [[nodiscard]] ResultCode SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count); - [[nodiscard]] ResultCode WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout); - [[nodiscard]] ResultCode WaitIfEqual(VAddr addr, s32 value, s64 timeout); + [[nodiscard]] Result Signal(VAddr addr, s32 count); + [[nodiscard]] Result SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count); + [[nodiscard]] Result SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count); + [[nodiscard]] Result WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout); + [[nodiscard]] Result WaitIfEqual(VAddr addr, s32 value, s64 timeout); ThreadTree thread_tree; diff --git a/src/core/hle/kernel/k_address_space_info.cpp b/src/core/hle/kernel/k_address_space_info.cpp index ca29edc88..3e612a207 100644 --- a/src/core/hle/kernel/k_address_space_info.cpp +++ b/src/core/hle/kernel/k_address_space_info.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> @@ -85,7 +84,7 @@ u64 KAddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) { ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index])); return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address; } - UNREACHABLE(); + ASSERT(false); return 0; } @@ -102,7 +101,7 @@ std::size_t KAddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type) ASSERT(IsAllowed39BitType(type)); return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size; } - UNREACHABLE(); + ASSERT(false); return 0; } diff --git a/src/core/hle/kernel/k_address_space_info.h b/src/core/hle/kernel/k_address_space_info.h index 06f31c6d5..69e9d77f2 100644 --- a/src/core/hle/kernel/k_address_space_info.h +++ b/src/core/hle/kernel/k_address_space_info.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_affinity_mask.h b/src/core/hle/kernel/k_affinity_mask.h index cf704ce87..b58716e90 100644 --- a/src/core/hle/kernel/k_affinity_mask.h +++ b/src/core/hle/kernel/k_affinity_mask.h @@ -1,9 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_auto_object.cpp b/src/core/hle/kernel/k_auto_object.cpp index c99a9ebb7..691af8ccb 100644 --- a/src/core/hle/kernel/k_auto_object.cpp +++ b/src/core/hle/kernel/k_auto_object.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/kernel.h" diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h index 05779f2d5..2827763d5 100644 --- a/src/core/hle/kernel/k_auto_object.h +++ b/src/core/hle/kernel/k_auto_object.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -19,7 +18,7 @@ namespace Kernel { class KernelCore; class KProcess; -#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \ +#define KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, ATTRIBUTE) \ \ private: \ friend class ::Kernel::KClassTokenGenerator; \ @@ -41,16 +40,19 @@ public: static constexpr const char* GetStaticTypeName() { \ return TypeName; \ } \ - virtual TypeObj GetTypeObj() const { \ + virtual TypeObj GetTypeObj() ATTRIBUTE { \ return GetStaticTypeObj(); \ } \ - virtual const char* GetTypeName() const { \ + virtual const char* GetTypeName() ATTRIBUTE { \ return GetStaticTypeName(); \ } \ \ private: \ constexpr bool operator!=(const TypeObj& rhs) +#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \ + KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, const override) + class KAutoObject { protected: class TypeObj { @@ -83,15 +85,13 @@ protected: }; private: - KERNEL_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject); + KERNEL_AUTOOBJECT_TRAITS_IMPL(KAutoObject, KAutoObject, const); public: explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) { RegisterWithKernel(); } - virtual ~KAutoObject() { - UnregisterWithKernel(); - } + virtual ~KAutoObject() = default; static KAutoObject* Create(KAutoObject* ptr); @@ -163,11 +163,12 @@ public: do { ASSERT(cur_ref_count > 0); } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, - std::memory_order_relaxed)); + std::memory_order_acq_rel)); // If ref count hits zero, destroy the object. if (cur_ref_count - 1 == 0) { this->Destroy(); + this->UnregisterWithKernel(); } } diff --git a/src/core/hle/kernel/k_auto_object_container.cpp b/src/core/hle/kernel/k_auto_object_container.cpp index d5f80d5b2..636b3f993 100644 --- a/src/core/hle/kernel/k_auto_object_container.cpp +++ b/src/core/hle/kernel/k_auto_object_container.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> diff --git a/src/core/hle/kernel/k_auto_object_container.h b/src/core/hle/kernel/k_auto_object_container.h index 697cc4289..badd75d2a 100644 --- a/src/core/hle/kernel/k_auto_object_container.h +++ b/src/core/hle/kernel/k_auto_object_container.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_class_token.cpp b/src/core/hle/kernel/k_class_token.cpp index 21e2fe494..cc2a0f7ca 100644 --- a/src/core/hle/kernel/k_class_token.cpp +++ b/src/core/hle/kernel/k_class_token.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_class_token.h" diff --git a/src/core/hle/kernel/k_class_token.h b/src/core/hle/kernel/k_class_token.h index 980010150..c9001ae3d 100644 --- a/src/core/hle/kernel/k_class_token.h +++ b/src/core/hle/kernel/k_class_token.h @@ -1,11 +1,8 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <atomic> - #include "common/bit_util.h" #include "common/common_types.h" @@ -52,6 +49,7 @@ private: } } } + UNREACHABLE(); }(); template <typename T> diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp index ef168fe87..3cb22ff4d 100644 --- a/src/core/hle/kernel/k_client_port.cpp +++ b/src/core/hle/kernel/k_client_port.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/scope_exit.h" #include "core/hle/kernel/hle_ipc.h" @@ -59,8 +58,8 @@ bool KClientPort::IsSignaled() const { return num_sessions < max_sessions; } -ResultCode KClientPort::CreateSession(KClientSession** out, - std::shared_ptr<SessionRequestManager> session_manager) { +Result KClientPort::CreateSession(KClientSession** out, + std::shared_ptr<SessionRequestManager> session_manager) { // Reserve a new session from the resource limit. KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(), LimitableResource::Sessions); diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h index 54bb05e20..e17eff28f 100644 --- a/src/core/hle/kernel/k_client_port.h +++ b/src/core/hle/kernel/k_client_port.h @@ -1,6 +1,5 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2016 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -53,8 +52,8 @@ public: void Destroy() override; bool IsSignaled() const override; - ResultCode CreateSession(KClientSession** out, - std::shared_ptr<SessionRequestManager> session_manager = nullptr); + Result CreateSession(KClientSession** out, + std::shared_ptr<SessionRequestManager> session_manager = nullptr); private: std::atomic<s32> num_sessions{}; diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp index 242582f8f..b2a887b14 100644 --- a/src/core/hle/kernel/k_client_session.cpp +++ b/src/core/hle/kernel/k_client_session.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_client_session.h" @@ -22,8 +21,8 @@ void KClientSession::Destroy() { void KClientSession::OnServerClosed() {} -ResultCode KClientSession::SendSyncRequest(KThread* thread, Core::Memory::Memory& memory, - Core::Timing::CoreTiming& core_timing) { +Result KClientSession::SendSyncRequest(KThread* thread, Core::Memory::Memory& memory, + Core::Timing::CoreTiming& core_timing) { // Signal the server session that new data is available return parent->GetServerSession().HandleSyncRequest(thread, memory, core_timing); } diff --git a/src/core/hle/kernel/k_client_session.h b/src/core/hle/kernel/k_client_session.h index ad6cc4ed1..0c750d756 100644 --- a/src/core/hle/kernel/k_client_session.h +++ b/src/core/hle/kernel/k_client_session.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,7 +9,7 @@ #include "core/hle/kernel/slab_helpers.h" #include "core/hle/result.h" -union ResultCode; +union Result; namespace Core::Memory { class Memory; @@ -47,8 +46,8 @@ public: return parent; } - ResultCode SendSyncRequest(KThread* thread, Core::Memory::Memory& memory, - Core::Timing::CoreTiming& core_timing); + Result SendSyncRequest(KThread* thread, Core::Memory::Memory& memory, + Core::Timing::CoreTiming& core_timing); void OnServerClosed(); diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp index 0b225e8e0..da57ceb21 100644 --- a/src/core/hle/kernel/k_code_memory.cpp +++ b/src/core/hle/kernel/k_code_memory.cpp @@ -1,15 +1,13 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/alignment.h" #include "common/common_types.h" #include "core/device_memory.h" -#include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_code_memory.h" #include "core/hle/kernel/k_light_lock.h" #include "core/hle/kernel/k_memory_block.h" -#include "core/hle/kernel/k_page_linked_list.h" +#include "core/hle/kernel/k_page_group.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/slab_helpers.h" @@ -21,7 +19,7 @@ namespace Kernel { KCodeMemory::KCodeMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_}, m_lock(kernel_) {} -ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) { +Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) { // Set members. m_owner = kernel.CurrentProcess(); @@ -29,10 +27,10 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr auto& page_table = m_owner->PageTable(); // Construct the page group. - m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize)); + m_page_group = {}; // Lock the memory. - R_TRY(page_table.LockForCodeMemory(addr, size)) + R_TRY(page_table.LockForCodeMemory(&m_page_group, addr, size)) // Clear the memory. for (const auto& block : m_page_group.Nodes()) { @@ -40,6 +38,7 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr } // Set remaining tracking members. + m_owner->Open(); m_address = addr; m_is_initialized = true; m_is_owner_mapped = false; @@ -53,11 +52,17 @@ void KCodeMemory::Finalize() { // Unlock. if (!m_is_mapped && !m_is_owner_mapped) { const size_t size = m_page_group.GetNumPages() * PageSize; - m_owner->PageTable().UnlockForCodeMemory(m_address, size); + m_owner->PageTable().UnlockForCodeMemory(m_address, size, m_page_group); } + + // Close the page group. + m_page_group = {}; + + // Close our reference to our owner. + m_owner->Close(); } -ResultCode KCodeMemory::Map(VAddr address, size_t size) { +Result KCodeMemory::Map(VAddr address, size_t size) { // Validate the size. R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); @@ -77,7 +82,7 @@ ResultCode KCodeMemory::Map(VAddr address, size_t size) { return ResultSuccess; } -ResultCode KCodeMemory::Unmap(VAddr address, size_t size) { +Result KCodeMemory::Unmap(VAddr address, size_t size) { // Validate the size. R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); @@ -94,7 +99,7 @@ ResultCode KCodeMemory::Unmap(VAddr address, size_t size) { return ResultSuccess; } -ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) { +Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) { // Validate the size. R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); @@ -114,7 +119,8 @@ ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermis k_perm = KMemoryPermission::UserReadExecute; break; default: - break; + // Already validated by ControlCodeMemory svc + UNREACHABLE(); } // Map the memory. @@ -127,7 +133,7 @@ ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermis return ResultSuccess; } -ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) { +Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) { // Validate the size. R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h index e0ba19a53..2e7e1436a 100644 --- a/src/core/hle/kernel/k_code_memory.h +++ b/src/core/hle/kernel/k_code_memory.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,7 +7,7 @@ #include "core/device_memory.h" #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_light_lock.h" -#include "core/hle/kernel/k_page_linked_list.h" +#include "core/hle/kernel/k_page_group.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/slab_helpers.h" #include "core/hle/kernel/svc_types.h" @@ -30,20 +29,20 @@ class KCodeMemory final public: explicit KCodeMemory(KernelCore& kernel_); - ResultCode Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size); - void Finalize(); + Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size); + void Finalize() override; - ResultCode Map(VAddr address, size_t size); - ResultCode Unmap(VAddr address, size_t size); - ResultCode MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm); - ResultCode UnmapFromOwner(VAddr address, size_t size); + Result Map(VAddr address, size_t size); + Result Unmap(VAddr address, size_t size); + Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm); + Result UnmapFromOwner(VAddr address, size_t size); - bool IsInitialized() const { + bool IsInitialized() const override { return m_is_initialized; } static void PostDestroy([[maybe_unused]] uintptr_t arg) {} - KProcess* GetOwner() const { + KProcess* GetOwner() const override { return m_owner; } VAddr GetSourceAddress() const { @@ -54,7 +53,7 @@ public: } private: - KPageLinkedList m_page_group{}; + KPageGroup m_page_group{}; KProcess* m_owner{}; VAddr m_address{}; KLightLock m_lock; diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp index aadcc297a..124149697 100644 --- a/src/core/hle/kernel/k_condition_variable.cpp +++ b/src/core/hle/kernel/k_condition_variable.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/arm/exclusive_monitor.h" #include "core/core.h" @@ -9,7 +8,6 @@ #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" -#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread_queue.h" #include "core/hle/kernel/kernel.h" @@ -63,8 +61,7 @@ public: explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel_) : KThreadQueue(kernel_) {} - void CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task) override { + void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Remove the thread as a waiter from its owner. waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread); @@ -82,8 +79,7 @@ public: KernelCore& kernel_, KConditionVariable::ThreadTree* t) : KThreadQueue(kernel_), m_tree(t) {} - void CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task) override { + void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Remove the thread as a waiter from its owner. if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) { owner->RemoveWaiter(waiting_thread); @@ -107,8 +103,8 @@ KConditionVariable::KConditionVariable(Core::System& system_) KConditionVariable::~KConditionVariable() = default; -ResultCode KConditionVariable::SignalToAddress(VAddr addr) { - KThread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread(); +Result KConditionVariable::SignalToAddress(VAddr addr) { + KThread* owner_thread = GetCurrentThreadPointer(kernel); // Signal the address. { @@ -128,7 +124,7 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) { } // Write the value to userspace. - ResultCode result{ResultSuccess}; + Result result{ResultSuccess}; if (WriteToUser(system, addr, std::addressof(next_value))) [[likely]] { result = ResultSuccess; } else { @@ -148,8 +144,8 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) { } } -ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) { - KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); +Result KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) { + KThread* cur_thread = GetCurrentThreadPointer(kernel); ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel); // Wait for the address. @@ -244,7 +240,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) { { KScopedSchedulerLock sl(kernel); - auto it = thread_tree.nfind_light({cv_key, -1}); + auto it = thread_tree.nfind_key({cv_key, -1}); while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetConditionVariableKey() == cv_key)) { KThread* target_thread = std::addressof(*it); @@ -263,7 +259,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) { } } -ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) { +Result KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) { // Prepare to wait. KThread* cur_thread = GetCurrentThreadPointer(kernel); ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue( diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h index 5e4815d08..fad4ed011 100644 --- a/src/core/hle/kernel/k_condition_variable.h +++ b/src/core/hle/kernel/k_condition_variable.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -26,12 +25,12 @@ public: ~KConditionVariable(); // Arbitration - [[nodiscard]] ResultCode SignalToAddress(VAddr addr); - [[nodiscard]] ResultCode WaitForAddress(Handle handle, VAddr addr, u32 value); + [[nodiscard]] Result SignalToAddress(VAddr addr); + [[nodiscard]] Result WaitForAddress(Handle handle, VAddr addr, u32 value); // Condition variable void Signal(u64 cv_key, s32 count); - [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout); + [[nodiscard]] Result Wait(VAddr addr, u64 key, u32 value, s64 timeout); private: void SignalImpl(KThread* thread); diff --git a/src/core/hle/kernel/k_event.cpp b/src/core/hle/kernel/k_event.cpp index 0720efece..e52fafbc7 100644 --- a/src/core/hle/kernel/k_event.cpp +++ b/src/core/hle/kernel/k_event.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_process.h" @@ -14,7 +13,7 @@ KEvent::KEvent(KernelCore& kernel_) KEvent::~KEvent() = default; -void KEvent::Initialize(std::string&& name_) { +void KEvent::Initialize(std::string&& name_, KProcess* owner_) { // Increment reference count. // Because reference count is one on creation, this will result // in a reference count of two. Thus, when both readable and @@ -30,10 +29,8 @@ void KEvent::Initialize(std::string&& name_) { writable_event.Initialize(this, name_ + ":Writable"); // Set our owner process. - owner = kernel.CurrentProcess(); - if (owner) { - owner->Open(); - } + owner = owner_; + owner->Open(); // Mark initialized. name = std::move(name_); @@ -47,10 +44,8 @@ void KEvent::Finalize() { void KEvent::PostDestroy(uintptr_t arg) { // Release the event count resource the owner process holds. KProcess* owner = reinterpret_cast<KProcess*>(arg); - if (owner) { - owner->GetResourceLimit()->Release(LimitableResource::Events, 1); - owner->Close(); - } + owner->GetResourceLimit()->Release(LimitableResource::Events, 1); + owner->Close(); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_event.h b/src/core/hle/kernel/k_event.h index 3d3ec99e2..2ff828feb 100644 --- a/src/core/hle/kernel/k_event.h +++ b/src/core/hle/kernel/k_event.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -22,7 +21,7 @@ public: explicit KEvent(KernelCore& kernel_); ~KEvent() override; - void Initialize(std::string&& name); + void Initialize(std::string&& name, KProcess* owner_); void Finalize() override; diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp index cf95f0852..e830ca46e 100644 --- a/src/core/hle/kernel/k_handle_table.cpp +++ b/src/core/hle/kernel/k_handle_table.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_handle_table.h" @@ -9,7 +8,7 @@ namespace Kernel { KHandleTable::KHandleTable(KernelCore& kernel_) : kernel{kernel_} {} KHandleTable::~KHandleTable() = default; -ResultCode KHandleTable::Finalize() { +Result KHandleTable::Finalize() { // Get the table and clear our record of it. u16 saved_table_size = 0; { @@ -63,7 +62,7 @@ bool KHandleTable::Remove(Handle handle) { return true; } -ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { +Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) { KScopedDisableDispatch dd(kernel); KScopedSpinLock lk(m_lock); @@ -75,7 +74,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { const auto linear_id = this->AllocateLinearId(); const auto index = this->AllocateEntry(); - m_entry_infos[index].info = {.linear_id = linear_id, .type = type}; + m_entry_infos[index].linear_id = linear_id; m_objects[index] = obj; obj->Open(); @@ -86,7 +85,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { return ResultSuccess; } -ResultCode KHandleTable::Reserve(Handle* out_handle) { +Result KHandleTable::Reserve(Handle* out_handle) { KScopedDisableDispatch dd(kernel); KScopedSpinLock lk(m_lock); @@ -116,7 +115,7 @@ void KHandleTable::Unreserve(Handle handle) { } } -void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { +void KHandleTable::Register(Handle handle, KAutoObject* obj) { KScopedDisableDispatch dd(kernel); KScopedSpinLock lk(m_lock); @@ -132,7 +131,7 @@ void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { // Set the entry. ASSERT(m_objects[index] == nullptr); - m_entry_infos[index].info = {.linear_id = static_cast<u16>(linear_id), .type = type}; + m_entry_infos[index].linear_id = static_cast<u16>(linear_id); m_objects[index] = obj; obj->Open(); diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h index 87004a0f9..0864a737c 100644 --- a/src/core/hle/kernel/k_handle_table.h +++ b/src/core/hle/kernel/k_handle_table.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -31,7 +30,7 @@ public: explicit KHandleTable(KernelCore& kernel_); ~KHandleTable(); - ResultCode Initialize(s32 size) { + Result Initialize(s32 size) { R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory); // Initialize all fields. @@ -42,7 +41,7 @@ public: m_free_head_index = -1; // Free all entries. - for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) { + for (s16 i = 0; i < static_cast<s16>(m_table_size); ++i) { m_objects[i] = nullptr; m_entry_infos[i].next_free_index = i - 1; m_free_head_index = i; @@ -61,7 +60,7 @@ public: return m_max_count; } - ResultCode Finalize(); + Result Finalize(); bool Remove(Handle handle); template <typename T = KAutoObject> @@ -101,20 +100,11 @@ public: return this->template GetObjectWithoutPseudoHandle<T>(handle); } - ResultCode Reserve(Handle* out_handle); + Result Reserve(Handle* out_handle); void Unreserve(Handle handle); - template <typename T> - ResultCode Add(Handle* out_handle, T* obj) { - static_assert(std::is_base_of_v<KAutoObject, T>); - return this->Add(out_handle, obj, obj->GetTypeObj().GetClassToken()); - } - - template <typename T> - void Register(Handle handle, T* obj) { - static_assert(std::is_base_of_v<KAutoObject, T>); - return this->Register(handle, obj, obj->GetTypeObj().GetClassToken()); - } + Result Add(Handle* out_handle, KAutoObject* obj); + void Register(Handle handle, KAutoObject* obj); template <typename T> bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const { @@ -160,9 +150,6 @@ public: } private: - ResultCode Add(Handle* out_handle, KAutoObject* obj, u16 type); - void Register(Handle handle, KAutoObject* obj, u16 type); - s32 AllocateEntry() { ASSERT(m_count < m_table_size); @@ -179,7 +166,7 @@ private: ASSERT(m_count > 0); m_objects[index] = nullptr; - m_entry_infos[index].next_free_index = m_free_head_index; + m_entry_infos[index].next_free_index = static_cast<s16>(m_free_head_index); m_free_head_index = index; @@ -278,19 +265,13 @@ private: } union EntryInfo { - struct { - u16 linear_id; - u16 type; - } info; - s32 next_free_index; + u16 linear_id; + s16 next_free_index; constexpr u16 GetLinearId() const { - return info.linear_id; - } - constexpr u16 GetType() const { - return info.type; + return linear_id; } - constexpr s32 GetNextFreeIndex() const { + constexpr s16 GetNextFreeIndex() const { return next_free_index; } }; diff --git a/src/core/hle/kernel/k_interrupt_manager.cpp b/src/core/hle/kernel/k_interrupt_manager.cpp index e5dd39751..1b577a5b3 100644 --- a/src/core/hle/kernel/k_interrupt_manager.cpp +++ b/src/core/hle/kernel/k_interrupt_manager.cpp @@ -1,12 +1,12 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_interrupt_manager.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/physical_core.h" namespace Kernel::KInterruptManager { @@ -16,8 +16,10 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) { return; } - auto& scheduler = kernel.Scheduler(core_id); - auto& current_thread = *scheduler.GetCurrentThread(); + // Acknowledge the interrupt. + kernel.PhysicalCore(core_id).ClearInterrupt(); + + auto& current_thread = GetCurrentThread(kernel); // If the user disable count is set, we may need to pin the current thread. if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) { @@ -27,8 +29,11 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) { process->PinCurrentThread(core_id); // Set the interrupt flag for the thread. - scheduler.GetCurrentThread()->SetInterruptFlag(); + GetCurrentThread(kernel).SetInterruptFlag(); } + + // Request interrupt scheduling. + kernel.CurrentScheduler()->RequestScheduleOnInterrupt(); } } // namespace Kernel::KInterruptManager diff --git a/src/core/hle/kernel/k_interrupt_manager.h b/src/core/hle/kernel/k_interrupt_manager.h index 05924801e..f103dfe3f 100644 --- a/src/core/hle/kernel/k_interrupt_manager.h +++ b/src/core/hle/kernel/k_interrupt_manager.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_light_condition_variable.cpp b/src/core/hle/kernel/k_light_condition_variable.cpp index a8001fffc..cade99cfd 100644 --- a/src/core/hle/kernel/k_light_condition_variable.cpp +++ b/src/core/hle/kernel/k_light_condition_variable.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_light_condition_variable.h" #include "core/hle/kernel/k_scheduler.h" @@ -18,8 +17,7 @@ public: bool term) : KThreadQueue(kernel_), m_wait_list(wl), m_allow_terminating_thread(term) {} - void CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task) override { + void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Only process waits if we're allowed to. if (ResultTerminationRequested == wait_result && m_allow_terminating_thread) { return; diff --git a/src/core/hle/kernel/k_light_condition_variable.h b/src/core/hle/kernel/k_light_condition_variable.h index 5d6d7f128..3cabd6b4f 100644 --- a/src/core/hle/kernel/k_light_condition_variable.h +++ b/src/core/hle/kernel/k_light_condition_variable.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_light_lock.cpp b/src/core/hle/kernel/k_light_lock.cpp index 4620342eb..43185320d 100644 --- a/src/core/hle/kernel/k_light_lock.cpp +++ b/src/core/hle/kernel/k_light_lock.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_light_lock.h" #include "core/hle/kernel/k_scheduler.h" @@ -16,8 +15,7 @@ class ThreadQueueImplForKLightLock final : public KThreadQueue { public: explicit ThreadQueueImplForKLightLock(KernelCore& kernel_) : KThreadQueue(kernel_) {} - void CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task) override { + void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Remove the thread as a waiter from its owner. if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) { owner->RemoveWaiter(waiting_thread); diff --git a/src/core/hle/kernel/k_light_lock.h b/src/core/hle/kernel/k_light_lock.h index 4163b8a85..7edd950c0 100644 --- a/src/core/hle/kernel/k_light_lock.h +++ b/src/core/hle/kernel/k_light_lock.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_linked_list.h b/src/core/hle/kernel/k_linked_list.h index 6adfe1e34..78859ced3 100644 --- a/src/core/hle/kernel/k_linked_list.h +++ b/src/core/hle/kernel/k_linked_list.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h index dcca47f1b..18df1f836 100644 --- a/src/core/hle/kernel/k_memory_block.h +++ b/src/core/hle/kernel/k_memory_block.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_memory_block_manager.cpp b/src/core/hle/kernel/k_memory_block_manager.cpp index fc7033564..3ddb9984f 100644 --- a/src/core/hle/kernel/k_memory_block_manager.cpp +++ b/src/core/hle/kernel/k_memory_block_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_memory_block_manager.h" #include "core/hle/kernel/memory_types.h" diff --git a/src/core/hle/kernel/k_memory_block_manager.h b/src/core/hle/kernel/k_memory_block_manager.h index d222da919..e14741b89 100644 --- a/src/core/hle/kernel/k_memory_block_manager.h +++ b/src/core/hle/kernel/k_memory_block_manager.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp b/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp index af652af58..098ba6eac 100644 --- a/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp +++ b/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/alignment.h" #include "common/literals.h" diff --git a/src/core/hle/kernel/k_memory_layout.cpp b/src/core/hle/kernel/k_memory_layout.cpp index fb1e2435f..55dc296d0 100644 --- a/src/core/hle/kernel/k_memory_layout.cpp +++ b/src/core/hle/kernel/k_memory_layout.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h index 57ff538cc..884fc623a 100644 --- a/src/core/hle/kernel/k_memory_layout.h +++ b/src/core/hle/kernel/k_memory_layout.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -57,11 +56,11 @@ constexpr std::size_t KernelPageTableHeapSize = GetMaximumOverheadSize(MainMemor constexpr std::size_t KernelInitialPageHeapSize = 128_KiB; constexpr std::size_t KernelSlabHeapDataSize = 5_MiB; -constexpr std::size_t KernelSlabHeapGapsSize = 2_MiB - 64_KiB; -constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize; +constexpr std::size_t KernelSlabHeapGapsSizeMax = 2_MiB - 64_KiB; +constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax; // NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860. -constexpr std::size_t KernelSlabHeapAdditionalSize = 416_KiB; +constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000; constexpr std::size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize; @@ -173,6 +172,10 @@ public: return Dereference(FindVirtualLinear(address)); } + const KMemoryRegion& GetPhysicalLinearRegion(PAddr address) const { + return Dereference(FindPhysicalLinear(address)); + } + const KMemoryRegion* GetPhysicalKernelTraceBufferRegion() const { return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_KernelTraceBuffer); } diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp index 1b44541b1..5b0a9963a 100644 --- a/src/core/hle/kernel/k_memory_manager.cpp +++ b/src/core/hle/kernel/k_memory_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> @@ -10,189 +9,411 @@ #include "common/scope_exit.h" #include "core/core.h" #include "core/device_memory.h" +#include "core/hle/kernel/initial_process.h" #include "core/hle/kernel/k_memory_manager.h" -#include "core/hle/kernel/k_page_linked_list.h" +#include "core/hle/kernel/k_page_group.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/svc_results.h" namespace Kernel { -KMemoryManager::KMemoryManager(Core::System& system_) : system{system_} {} +namespace { + +constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) { + if ((type | KMemoryRegionType_DramApplicationPool) == type) { + return KMemoryManager::Pool::Application; + } else if ((type | KMemoryRegionType_DramAppletPool) == type) { + return KMemoryManager::Pool::Applet; + } else if ((type | KMemoryRegionType_DramSystemPool) == type) { + return KMemoryManager::Pool::System; + } else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) { + return KMemoryManager::Pool::SystemNonSecure; + } else { + ASSERT_MSG(false, "InvalidMemoryRegionType for conversion to Pool"); + return {}; + } +} -std::size_t KMemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) { - const auto size{end_address - start_address}; +} // namespace + +KMemoryManager::KMemoryManager(Core::System& system_) + : system{system_}, pool_locks{ + KLightLock{system_.Kernel()}, + KLightLock{system_.Kernel()}, + KLightLock{system_.Kernel()}, + KLightLock{system_.Kernel()}, + } {} + +void KMemoryManager::Initialize(VAddr management_region, size_t management_region_size) { + + // Clear the management region to zero. + const VAddr management_region_end = management_region + management_region_size; + + // Reset our manager count. + num_managers = 0; + + // Traverse the virtual memory layout tree, initializing each manager as appropriate. + while (num_managers != MaxManagerCount) { + // Locate the region that should initialize the current manager. + PAddr region_address = 0; + size_t region_size = 0; + Pool region_pool = Pool::Count; + for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) { + // We only care about regions that we need to create managers for. + if (!it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) { + continue; + } - // Calculate metadata sizes - const auto ref_count_size{(size / PageSize) * sizeof(u16)}; - const auto optimize_map_size{(Common::AlignUp((size / PageSize), 64) / 64) * sizeof(u64)}; - const auto manager_size{Common::AlignUp(optimize_map_size + ref_count_size, PageSize)}; - const auto page_heap_size{KPageHeap::CalculateManagementOverheadSize(size)}; - const auto total_metadata_size{manager_size + page_heap_size}; - ASSERT(manager_size <= total_metadata_size); - ASSERT(Common::IsAligned(total_metadata_size, PageSize)); + // We want to initialize the managers in order. + if (it.GetAttributes() != num_managers) { + continue; + } - // Setup region - pool = new_pool; + const PAddr cur_start = it.GetAddress(); + const PAddr cur_end = it.GetEndAddress(); + + // Validate the region. + ASSERT(cur_end != 0); + ASSERT(cur_start != 0); + ASSERT(it.GetSize() > 0); + + // Update the region's extents. + if (region_address == 0) { + region_address = cur_start; + region_size = it.GetSize(); + region_pool = GetPoolFromMemoryRegionType(it.GetType()); + } else { + ASSERT(cur_start == region_address + region_size); + + // Update the size. + region_size = cur_end - region_address; + ASSERT(GetPoolFromMemoryRegionType(it.GetType()) == region_pool); + } + } + + // If we didn't find a region, we're done. + if (region_size == 0) { + break; + } - // Initialize the manager's KPageHeap - heap.Initialize(start_address, size, page_heap_size); + // Initialize a new manager for the region. + Impl* manager = std::addressof(managers[num_managers++]); + ASSERT(num_managers <= managers.size()); + + const size_t cur_size = manager->Initialize(region_address, region_size, management_region, + management_region_end, region_pool); + management_region += cur_size; + ASSERT(management_region <= management_region_end); + + // Insert the manager into the pool list. + const auto region_pool_index = static_cast<u32>(region_pool); + if (pool_managers_tail[region_pool_index] == nullptr) { + pool_managers_head[region_pool_index] = manager; + } else { + pool_managers_tail[region_pool_index]->SetNext(manager); + manager->SetPrev(pool_managers_tail[region_pool_index]); + } + pool_managers_tail[region_pool_index] = manager; + } - // Free the memory to the heap - heap.Free(start_address, size / PageSize); + // Free each region to its corresponding heap. + size_t reserved_sizes[MaxManagerCount] = {}; + const PAddr ini_start = GetInitialProcessBinaryPhysicalAddress(); + const PAddr ini_end = ini_start + InitialProcessBinarySizeMax; + const PAddr ini_last = ini_end - 1; + for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) { + if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) { + // Get the manager for the region. + auto index = it.GetAttributes(); + auto& manager = managers[index]; + + const PAddr cur_start = it.GetAddress(); + const PAddr cur_last = it.GetLastAddress(); + const PAddr cur_end = it.GetEndAddress(); + + if (cur_start <= ini_start && ini_last <= cur_last) { + // Free memory before the ini to the heap. + if (cur_start != ini_start) { + manager.Free(cur_start, (ini_start - cur_start) / PageSize); + } - // Update the heap's used size - heap.UpdateUsedSize(); + // Open/reserve the ini memory. + manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize); + reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax; - return total_metadata_size; -} + // Free memory after the ini to the heap. + if (ini_last != cur_last) { + ASSERT(cur_end != 0); + manager.Free(ini_end, cur_end - ini_end); + } + } else { + // Ensure there's no partial overlap with the ini image. + if (cur_start <= ini_last) { + ASSERT(cur_last < ini_start); + } else { + // Otherwise, check the region for general validity. + ASSERT(cur_end != 0); + } -void KMemoryManager::InitializeManager(Pool pool, u64 start_address, u64 end_address) { - ASSERT(pool < Pool::Count); - managers[static_cast<std::size_t>(pool)].Initialize(pool, start_address, end_address); + // Free the memory to the heap. + manager.Free(cur_start, it.GetSize() / PageSize); + } + } + } + + // Update the used size for all managers. + for (size_t i = 0; i < num_managers; ++i) { + managers[i].SetInitialUsedHeapSize(reserved_sizes[i]); + } } -VAddr KMemoryManager::AllocateAndOpenContinuous(std::size_t num_pages, std::size_t align_pages, - u32 option) { - // Early return if we're allocating no pages +PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option) { + // Early return if we're allocating no pages. if (num_pages == 0) { - return {}; + return 0; } - // Lock the pool that we're allocating from + // Lock the pool that we're allocating from. const auto [pool, dir] = DecodeOption(option); - const auto pool_index{static_cast<std::size_t>(pool)}; - std::lock_guard lock{pool_locks[pool_index]}; - - // Choose a heap based on our page size request - const s32 heap_index{KPageHeap::GetAlignedBlockIndex(num_pages, align_pages)}; - - // Loop, trying to iterate from each block - // TODO (bunnei): Support multiple managers - Impl& chosen_manager{managers[pool_index]}; - VAddr allocated_block{chosen_manager.AllocateBlock(heap_index, false)}; + KScopedLightLock lk(pool_locks[static_cast<std::size_t>(pool)]); + + // Choose a heap based on our page size request. + const s32 heap_index = KPageHeap::GetAlignedBlockIndex(num_pages, align_pages); + + // Loop, trying to iterate from each block. + Impl* chosen_manager = nullptr; + PAddr allocated_block = 0; + for (chosen_manager = this->GetFirstManager(pool, dir); chosen_manager != nullptr; + chosen_manager = this->GetNextManager(chosen_manager, dir)) { + allocated_block = chosen_manager->AllocateBlock(heap_index, true); + if (allocated_block != 0) { + break; + } + } - // If we failed to allocate, quit now - if (!allocated_block) { - return {}; + // If we failed to allocate, quit now. + if (allocated_block == 0) { + return 0; } - // If we allocated more than we need, free some - const auto allocated_pages{KPageHeap::GetBlockNumPages(heap_index)}; + // If we allocated more than we need, free some. + const size_t allocated_pages = KPageHeap::GetBlockNumPages(heap_index); if (allocated_pages > num_pages) { - chosen_manager.Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages); + chosen_manager->Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages); } + // Open the first reference to the pages. + chosen_manager->OpenFirst(allocated_block, num_pages); + return allocated_block; } -ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, - Direction dir, u32 heap_fill_value) { - ASSERT(page_list.GetNumPages() == 0); +Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool, + Direction dir, bool random) { + // Choose a heap based on our page size request. + const s32 heap_index = KPageHeap::GetBlockIndex(num_pages); + R_UNLESS(0 <= heap_index, ResultOutOfMemory); + + // Ensure that we don't leave anything un-freed. + auto group_guard = SCOPE_GUARD({ + for (const auto& it : out->Nodes()) { + auto& manager = this->GetManager(system.Kernel().MemoryLayout(), it.GetAddress()); + const size_t num_pages_to_free = + std::min(it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize); + manager.Free(it.GetAddress(), num_pages_to_free); + } + }); - // Early return if we're allocating no pages - if (num_pages == 0) { - return ResultSuccess; - } + // Keep allocating until we've allocated all our pages. + for (s32 index = heap_index; index >= 0 && num_pages > 0; index--) { + const size_t pages_per_alloc = KPageHeap::GetBlockNumPages(index); + for (Impl* cur_manager = this->GetFirstManager(pool, dir); cur_manager != nullptr; + cur_manager = this->GetNextManager(cur_manager, dir)) { + while (num_pages >= pages_per_alloc) { + // Allocate a block. + PAddr allocated_block = cur_manager->AllocateBlock(index, random); + if (allocated_block == 0) { + break; + } - // Lock the pool that we're allocating from - const auto pool_index{static_cast<std::size_t>(pool)}; - std::lock_guard lock{pool_locks[pool_index]}; + // Safely add it to our group. + { + auto block_guard = + SCOPE_GUARD({ cur_manager->Free(allocated_block, pages_per_alloc); }); + R_TRY(out->AddBlock(allocated_block, pages_per_alloc)); + block_guard.Cancel(); + } - // Choose a heap based on our page size request - const s32 heap_index{KPageHeap::GetBlockIndex(num_pages)}; - if (heap_index < 0) { - return ResultOutOfMemory; + num_pages -= pages_per_alloc; + } + } } - // TODO (bunnei): Support multiple managers - Impl& chosen_manager{managers[pool_index]}; + // Only succeed if we allocated as many pages as we wanted. + R_UNLESS(num_pages == 0, ResultOutOfMemory); - // Ensure that we don't leave anything un-freed - auto group_guard = detail::ScopeExit([&] { - for (const auto& it : page_list.Nodes()) { - const auto min_num_pages{std::min<size_t>( - it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)}; - chosen_manager.Free(it.GetAddress(), min_num_pages); - } - }); + // We succeeded! + group_guard.Cancel(); + return ResultSuccess; +} - // Keep allocating until we've allocated all our pages - for (s32 index{heap_index}; index >= 0 && num_pages > 0; index--) { - const auto pages_per_alloc{KPageHeap::GetBlockNumPages(index)}; +Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option) { + ASSERT(out != nullptr); + ASSERT(out->GetNumPages() == 0); - while (num_pages >= pages_per_alloc) { - // Allocate a block - VAddr allocated_block{chosen_manager.AllocateBlock(index, false)}; - if (!allocated_block) { - break; - } + // Early return if we're allocating no pages. + R_SUCCEED_IF(num_pages == 0); - // Safely add it to our group - { - auto block_guard = detail::ScopeExit( - [&] { chosen_manager.Free(allocated_block, pages_per_alloc); }); + // Lock the pool that we're allocating from. + const auto [pool, dir] = DecodeOption(option); + KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]); + + // Allocate the page group. + R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false)); + + // Open the first reference to the pages. + for (const auto& block : out->Nodes()) { + PAddr cur_address = block.GetAddress(); + size_t remaining_pages = block.GetNumPages(); + while (remaining_pages > 0) { + // Get the manager for the current address. + auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address); + + // Process part or all of the block. + const size_t cur_pages = + std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address)); + manager.OpenFirst(cur_address, cur_pages); + + // Advance. + cur_address += cur_pages * PageSize; + remaining_pages -= cur_pages; + } + } - if (const ResultCode result{page_list.AddBlock(allocated_block, pages_per_alloc)}; - result.IsError()) { - return result; - } + return ResultSuccess; +} - block_guard.Cancel(); - } +Result KMemoryManager::AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option, + u64 process_id, u8 fill_pattern) { + ASSERT(out != nullptr); + ASSERT(out->GetNumPages() == 0); - num_pages -= pages_per_alloc; - } - } + // Decode the option. + const auto [pool, dir] = DecodeOption(option); - // Clear allocated memory. - for (const auto& it : page_list.Nodes()) { - std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value, - it.GetSize()); + // Allocate the memory. + { + // Lock the pool that we're allocating from. + KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]); + + // Allocate the page group. + R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false)); + + // Open the first reference to the pages. + for (const auto& block : out->Nodes()) { + PAddr cur_address = block.GetAddress(); + size_t remaining_pages = block.GetNumPages(); + while (remaining_pages > 0) { + // Get the manager for the current address. + auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address); + + // Process part or all of the block. + const size_t cur_pages = + std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address)); + manager.OpenFirst(cur_address, cur_pages); + + // Advance. + cur_address += cur_pages * PageSize; + remaining_pages -= cur_pages; + } + } } - // Only succeed if we allocated as many pages as we wanted - if (num_pages) { - return ResultOutOfMemory; + // Set all the allocated memory. + for (const auto& block : out->Nodes()) { + std::memset(system.DeviceMemory().GetPointer(block.GetAddress()), fill_pattern, + block.GetSize()); } - // We succeeded! - group_guard.Cancel(); - return ResultSuccess; } -ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, - Direction dir, u32 heap_fill_value) { - // Early return if we're freeing no pages - if (!num_pages) { - return ResultSuccess; +void KMemoryManager::Open(PAddr address, size_t num_pages) { + // Repeatedly open references until we've done so for all pages. + while (num_pages) { + auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address); + const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address)); + + { + KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]); + manager.Open(address, cur_pages); + } + + num_pages -= cur_pages; + address += cur_pages * PageSize; } +} - // Lock the pool that we're freeing from - const auto pool_index{static_cast<std::size_t>(pool)}; - std::lock_guard lock{pool_locks[pool_index]}; +void KMemoryManager::Close(PAddr address, size_t num_pages) { + // Repeatedly close references until we've done so for all pages. + while (num_pages) { + auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address); + const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address)); - // TODO (bunnei): Support multiple managers - Impl& chosen_manager{managers[pool_index]}; + { + KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]); + manager.Close(address, cur_pages); + } - // Free all of the pages - for (const auto& it : page_list.Nodes()) { - const auto min_num_pages{std::min<size_t>( - it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)}; - chosen_manager.Free(it.GetAddress(), min_num_pages); + num_pages -= cur_pages; + address += cur_pages * PageSize; } +} - return ResultSuccess; +void KMemoryManager::Close(const KPageGroup& pg) { + for (const auto& node : pg.Nodes()) { + Close(node.GetAddress(), node.GetNumPages()); + } +} +void KMemoryManager::Open(const KPageGroup& pg) { + for (const auto& node : pg.Nodes()) { + Open(node.GetAddress(), node.GetNumPages()); + } +} + +size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr management, + VAddr management_end, Pool p) { + // Calculate management sizes. + const size_t ref_count_size = (size / PageSize) * sizeof(u16); + const size_t optimize_map_size = CalculateOptimizedProcessOverheadSize(size); + const size_t manager_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize); + const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(size); + const size_t total_management_size = manager_size + page_heap_size; + ASSERT(manager_size <= total_management_size); + ASSERT(management + total_management_size <= management_end); + ASSERT(Common::IsAligned(total_management_size, PageSize)); + + // Setup region. + pool = p; + management_region = management; + page_reference_counts.resize( + Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize() / PageSize); + ASSERT(Common::IsAligned(management_region, PageSize)); + + // Initialize the manager's KPageHeap. + heap.Initialize(address, size, management + manager_size, page_heap_size); + + return total_management_size; } -std::size_t KMemoryManager::Impl::CalculateManagementOverheadSize(std::size_t region_size) { - const std::size_t ref_count_size = (region_size / PageSize) * sizeof(u16); - const std::size_t optimize_map_size = +size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) { + const size_t ref_count_size = (region_size / PageSize) * sizeof(u16); + const size_t optimize_map_size = (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) / Common::BitSize<u64>()) * sizeof(u64); - const std::size_t manager_meta_size = - Common::AlignUp(optimize_map_size + ref_count_size, PageSize); - const std::size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size); + const size_t manager_meta_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize); + const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size); return manager_meta_size + page_heap_size; } diff --git a/src/core/hle/kernel/k_memory_manager.h b/src/core/hle/kernel/k_memory_manager.h index 17c7690f1..dcb9b6348 100644 --- a/src/core/hle/kernel/k_memory_manager.h +++ b/src/core/hle/kernel/k_memory_manager.h @@ -1,15 +1,15 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <array> -#include <mutex> #include <tuple> #include "common/common_funcs.h" #include "common/common_types.h" +#include "core/hle/kernel/k_light_lock.h" +#include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_page_heap.h" #include "core/hle/result.h" @@ -19,7 +19,7 @@ class System; namespace Kernel { -class KPageLinkedList; +class KPageGroup; class KMemoryManager final { public: @@ -52,22 +52,33 @@ public: explicit KMemoryManager(Core::System& system_); - constexpr std::size_t GetSize(Pool pool) const { - return managers[static_cast<std::size_t>(pool)].GetSize(); + void Initialize(VAddr management_region, size_t management_region_size); + + constexpr size_t GetSize(Pool pool) const { + constexpr Direction GetSizeDirection = Direction::FromFront; + size_t total = 0; + for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr; + manager = this->GetNextManager(manager, GetSizeDirection)) { + total += manager->GetSize(); + } + return total; } - void InitializeManager(Pool pool, u64 start_address, u64 end_address); + PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option); + Result AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option); + Result AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option, u64 process_id, + u8 fill_pattern); + + static constexpr size_t MaxManagerCount = 10; - VAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option); - ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir, - u32 heap_fill_value = 0); - ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir, - u32 heap_fill_value = 0); + void Close(PAddr address, size_t num_pages); + void Close(const KPageGroup& pg); - static constexpr std::size_t MaxManagerCount = 10; + void Open(PAddr address, size_t num_pages); + void Open(const KPageGroup& pg); public: - static std::size_t CalculateManagementOverheadSize(std::size_t region_size) { + static size_t CalculateManagementOverheadSize(size_t region_size) { return Impl::CalculateManagementOverheadSize(region_size); } @@ -100,17 +111,26 @@ private: Impl() = default; ~Impl() = default; - std::size_t Initialize(Pool new_pool, u64 start_address, u64 end_address); + size_t Initialize(PAddr address, size_t size, VAddr management, VAddr management_end, + Pool p); VAddr AllocateBlock(s32 index, bool random) { return heap.AllocateBlock(index, random); } - void Free(VAddr addr, std::size_t num_pages) { + void Free(VAddr addr, size_t num_pages) { heap.Free(addr, num_pages); } - constexpr std::size_t GetSize() const { + void SetInitialUsedHeapSize(size_t reserved_size) { + heap.SetInitialUsedSize(reserved_size); + } + + constexpr Pool GetPool() const { + return pool; + } + + constexpr size_t GetSize() const { return heap.GetSize(); } @@ -122,10 +142,88 @@ private: return heap.GetEndAddress(); } - static std::size_t CalculateManagementOverheadSize(std::size_t region_size); + constexpr size_t GetPageOffset(PAddr address) const { + return heap.GetPageOffset(address); + } + + constexpr size_t GetPageOffsetToEnd(PAddr address) const { + return heap.GetPageOffsetToEnd(address); + } + + constexpr void SetNext(Impl* n) { + next = n; + } + + constexpr void SetPrev(Impl* n) { + prev = n; + } + + constexpr Impl* GetNext() const { + return next; + } + + constexpr Impl* GetPrev() const { + return prev; + } + + void OpenFirst(PAddr address, size_t num_pages) { + size_t index = this->GetPageOffset(address); + const size_t end = index + num_pages; + while (index < end) { + const RefCount ref_count = (++page_reference_counts[index]); + ASSERT(ref_count == 1); - static constexpr std::size_t CalculateOptimizedProcessOverheadSize( - std::size_t region_size) { + index++; + } + } + + void Open(PAddr address, size_t num_pages) { + size_t index = this->GetPageOffset(address); + const size_t end = index + num_pages; + while (index < end) { + const RefCount ref_count = (++page_reference_counts[index]); + ASSERT(ref_count > 1); + + index++; + } + } + + void Close(PAddr address, size_t num_pages) { + size_t index = this->GetPageOffset(address); + const size_t end = index + num_pages; + + size_t free_start = 0; + size_t free_count = 0; + while (index < end) { + ASSERT(page_reference_counts[index] > 0); + const RefCount ref_count = (--page_reference_counts[index]); + + // Keep track of how many zero refcounts we see in a row, to minimize calls to free. + if (ref_count == 0) { + if (free_count > 0) { + free_count++; + } else { + free_start = index; + free_count = 1; + } + } else { + if (free_count > 0) { + this->Free(heap.GetAddress() + free_start * PageSize, free_count); + free_count = 0; + } + } + + index++; + } + + if (free_count > 0) { + this->Free(heap.GetAddress() + free_start * PageSize, free_count); + } + } + + static size_t CalculateManagementOverheadSize(size_t region_size); + + static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) { return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) / Common::BitSize<u64>()) * sizeof(u64); @@ -135,13 +233,45 @@ private: using RefCount = u16; KPageHeap heap; + std::vector<RefCount> page_reference_counts; + VAddr management_region{}; Pool pool{}; + Impl* next{}; + Impl* prev{}; }; private: + Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) { + return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()]; + } + + const Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) const { + return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()]; + } + + constexpr Impl* GetFirstManager(Pool pool, Direction dir) const { + return dir == Direction::FromBack ? pool_managers_tail[static_cast<size_t>(pool)] + : pool_managers_head[static_cast<size_t>(pool)]; + } + + constexpr Impl* GetNextManager(Impl* cur, Direction dir) const { + if (dir == Direction::FromBack) { + return cur->GetPrev(); + } else { + return cur->GetNext(); + } + } + + Result AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool, Direction dir, + bool random); + +private: Core::System& system; - std::array<std::mutex, static_cast<std::size_t>(Pool::Count)> pool_locks; + std::array<KLightLock, static_cast<size_t>(Pool::Count)> pool_locks; + std::array<Impl*, MaxManagerCount> pool_managers_head{}; + std::array<Impl*, MaxManagerCount> pool_managers_tail{}; std::array<Impl, MaxManagerCount> managers; + size_t num_managers{}; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_memory_region.h b/src/core/hle/kernel/k_memory_region.h index e9bdf4e59..5037e657f 100644 --- a/src/core/hle/kernel/k_memory_region.h +++ b/src/core/hle/kernel/k_memory_region.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_memory_region_type.h b/src/core/hle/kernel/k_memory_region_type.h index a05e66677..7e2fcccdc 100644 --- a/src/core/hle/kernel/k_memory_region_type.h +++ b/src/core/hle/kernel/k_memory_region_type.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -14,7 +13,8 @@ namespace Kernel { enum KMemoryRegionType : u32 { - KMemoryRegionAttr_CarveoutProtected = 0x04000000, + KMemoryRegionAttr_CarveoutProtected = 0x02000000, + KMemoryRegionAttr_Uncached = 0x04000000, KMemoryRegionAttr_DidKernelMap = 0x08000000, KMemoryRegionAttr_ShouldKernelMap = 0x10000000, KMemoryRegionAttr_UserReadOnly = 0x20000000, @@ -239,6 +239,11 @@ static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A); static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A); static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A); +// UNUSED: .DeriveSparse(2, 2, 0); +constexpr auto KMemoryRegionType_VirtualDramUnknownDebug = + KMemoryRegionType_Dram.DeriveSparse(2, 2, 1); +static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52)); + constexpr auto KMemoryRegionType_VirtualDramKernelInitPt = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0); constexpr auto KMemoryRegionType_VirtualDramPoolManagement = @@ -330,6 +335,8 @@ constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) { return KMemoryRegionType_VirtualDramKernelTraceBuffer; } else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) { return KMemoryRegionType_VirtualDramKernelPtHeap; + } else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) { + return KMemoryRegionType_VirtualDramUnknownDebug; } else { return KMemoryRegionType_Dram; } diff --git a/src/core/hle/kernel/k_page_bitmap.h b/src/core/hle/kernel/k_page_bitmap.h index c75d667c9..c97b3dc0b 100644 --- a/src/core/hle/kernel/k_page_bitmap.h +++ b/src/core/hle/kernel/k_page_bitmap.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_page_buffer.cpp b/src/core/hle/kernel/k_page_buffer.cpp new file mode 100644 index 000000000..1a0bf4439 --- /dev/null +++ b/src/core/hle/kernel/k_page_buffer.cpp @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/alignment.h" +#include "common/assert.h" +#include "core/core.h" +#include "core/device_memory.h" +#include "core/hle/kernel/k_page_buffer.h" +#include "core/hle/kernel/memory_types.h" + +namespace Kernel { + +KPageBuffer* KPageBuffer::FromPhysicalAddress(Core::System& system, PAddr phys_addr) { + ASSERT(Common::IsAligned(phys_addr, PageSize)); + return reinterpret_cast<KPageBuffer*>(system.DeviceMemory().GetPointer(phys_addr)); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_page_buffer.h b/src/core/hle/kernel/k_page_buffer.h new file mode 100644 index 000000000..7e50dc1d1 --- /dev/null +++ b/src/core/hle/kernel/k_page_buffer.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "common/common_types.h" +#include "core/hle/kernel/memory_types.h" +#include "core/hle/kernel/slab_helpers.h" + +namespace Kernel { + +class KPageBuffer final : public KSlabAllocated<KPageBuffer> { +public: + KPageBuffer() = default; + + static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr); + +private: + [[maybe_unused]] alignas(PageSize) std::array<u8, PageSize> m_buffer{}; +}; + +static_assert(sizeof(KPageBuffer) == PageSize); +static_assert(alignof(KPageBuffer) == PageSize); + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_page_linked_list.h b/src/core/hle/kernel/k_page_group.h index 0e2ae582a..968753992 100644 --- a/src/core/hle/kernel/k_page_linked_list.h +++ b/src/core/hle/kernel/k_page_group.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -13,7 +12,7 @@ namespace Kernel { -class KPageLinkedList final { +class KPageGroup final { public: class Node final { public: @@ -37,8 +36,8 @@ public: }; public: - KPageLinkedList() = default; - KPageLinkedList(u64 address, u64 num_pages) { + KPageGroup() = default; + KPageGroup(u64 address, u64 num_pages) { ASSERT(AddBlock(address, num_pages).IsSuccess()); } @@ -58,7 +57,7 @@ public: return num_pages; } - bool IsEqual(KPageLinkedList& other) const { + bool IsEqual(KPageGroup& other) const { auto this_node = nodes.begin(); auto other_node = other.nodes.begin(); while (this_node != nodes.end() && other_node != other.nodes.end()) { @@ -73,7 +72,7 @@ public: return this_node == nodes.end() && other_node == other.nodes.end(); } - ResultCode AddBlock(u64 address, u64 num_pages) { + Result AddBlock(u64 address, u64 num_pages) { if (!num_pages) { return ResultSuccess; } @@ -89,6 +88,10 @@ public: return ResultSuccess; } + bool Empty() const { + return nodes.empty(); + } + private: std::list<Node> nodes; }; diff --git a/src/core/hle/kernel/k_page_heap.cpp b/src/core/hle/kernel/k_page_heap.cpp index 29d996d62..5ede60168 100644 --- a/src/core/hle/kernel/k_page_heap.cpp +++ b/src/core/hle/kernel/k_page_heap.cpp @@ -1,41 +1,56 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/hle/kernel/k_page_heap.h" namespace Kernel { -void KPageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_size) { - // Check our assumptions - ASSERT(Common::IsAligned((address), PageSize)); +void KPageHeap::Initialize(PAddr address, size_t size, VAddr management_address, + size_t management_size, const size_t* block_shifts, + size_t num_block_shifts) { + // Check our assumptions. + ASSERT(Common::IsAligned(address, PageSize)); ASSERT(Common::IsAligned(size, PageSize)); + ASSERT(0 < num_block_shifts && num_block_shifts <= NumMemoryBlockPageShifts); + const VAddr management_end = management_address + management_size; - // Set our members - heap_address = address; - heap_size = size; - - // Setup bitmaps - metadata.resize(metadata_size / sizeof(u64)); - u64* cur_bitmap_storage{metadata.data()}; - for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) { - const std::size_t cur_block_shift{MemoryBlockPageShifts[i]}; - const std::size_t next_block_shift{ - (i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0}; - cur_bitmap_storage = blocks[i].Initialize(heap_address, heap_size, cur_block_shift, - next_block_shift, cur_bitmap_storage); + // Set our members. + m_heap_address = address; + m_heap_size = size; + m_num_blocks = num_block_shifts; + + // Setup bitmaps. + m_management_data.resize(management_size / sizeof(u64)); + u64* cur_bitmap_storage{m_management_data.data()}; + for (size_t i = 0; i < num_block_shifts; i++) { + const size_t cur_block_shift = block_shifts[i]; + const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0; + cur_bitmap_storage = m_blocks[i].Initialize(m_heap_address, m_heap_size, cur_block_shift, + next_block_shift, cur_bitmap_storage); } + + // Ensure we didn't overextend our bounds. + ASSERT(VAddr(cur_bitmap_storage) <= management_end); +} + +size_t KPageHeap::GetNumFreePages() const { + size_t num_free = 0; + + for (size_t i = 0; i < m_num_blocks; i++) { + num_free += m_blocks[i].GetNumFreePages(); + } + + return num_free; } -VAddr KPageHeap::AllocateBlock(s32 index, bool random) { - const std::size_t needed_size{blocks[index].GetSize()}; +PAddr KPageHeap::AllocateBlock(s32 index, bool random) { + const size_t needed_size = m_blocks[index].GetSize(); - for (s32 i{index}; i < static_cast<s32>(MemoryBlockPageShifts.size()); i++) { - if (const VAddr addr{blocks[i].PopBlock(random)}; addr) { - if (const std::size_t allocated_size{blocks[i].GetSize()}; - allocated_size > needed_size) { - Free(addr + needed_size, (allocated_size - needed_size) / PageSize); + for (s32 i = index; i < static_cast<s32>(m_num_blocks); i++) { + if (const PAddr addr = m_blocks[i].PopBlock(random); addr != 0) { + if (const size_t allocated_size = m_blocks[i].GetSize(); allocated_size > needed_size) { + this->Free(addr + needed_size, (allocated_size - needed_size) / PageSize); } return addr; } @@ -44,34 +59,34 @@ VAddr KPageHeap::AllocateBlock(s32 index, bool random) { return 0; } -void KPageHeap::FreeBlock(VAddr block, s32 index) { +void KPageHeap::FreeBlock(PAddr block, s32 index) { do { - block = blocks[index++].PushBlock(block); + block = m_blocks[index++].PushBlock(block); } while (block != 0); } -void KPageHeap::Free(VAddr addr, std::size_t num_pages) { - // Freeing no pages is a no-op +void KPageHeap::Free(PAddr addr, size_t num_pages) { + // Freeing no pages is a no-op. if (num_pages == 0) { return; } - // Find the largest block size that we can free, and free as many as possible - s32 big_index{static_cast<s32>(MemoryBlockPageShifts.size()) - 1}; - const VAddr start{addr}; - const VAddr end{(num_pages * PageSize) + addr}; - VAddr before_start{start}; - VAddr before_end{start}; - VAddr after_start{end}; - VAddr after_end{end}; + // Find the largest block size that we can free, and free as many as possible. + s32 big_index = static_cast<s32>(m_num_blocks) - 1; + const PAddr start = addr; + const PAddr end = addr + num_pages * PageSize; + PAddr before_start = start; + PAddr before_end = start; + PAddr after_start = end; + PAddr after_end = end; while (big_index >= 0) { - const std::size_t block_size{blocks[big_index].GetSize()}; - const VAddr big_start{Common::AlignUp((start), block_size)}; - const VAddr big_end{Common::AlignDown((end), block_size)}; + const size_t block_size = m_blocks[big_index].GetSize(); + const PAddr big_start = Common::AlignUp(start, block_size); + const PAddr big_end = Common::AlignDown(end, block_size); if (big_start < big_end) { - // Free as many big blocks as we can - for (auto block{big_start}; block < big_end; block += block_size) { - FreeBlock(block, big_index); + // Free as many big blocks as we can. + for (auto block = big_start; block < big_end; block += block_size) { + this->FreeBlock(block, big_index); } before_end = big_start; after_start = big_end; @@ -81,31 +96,31 @@ void KPageHeap::Free(VAddr addr, std::size_t num_pages) { } ASSERT(big_index >= 0); - // Free space before the big blocks - for (s32 i{big_index - 1}; i >= 0; i--) { - const std::size_t block_size{blocks[i].GetSize()}; + // Free space before the big blocks. + for (s32 i = big_index - 1; i >= 0; i--) { + const size_t block_size = m_blocks[i].GetSize(); while (before_start + block_size <= before_end) { before_end -= block_size; - FreeBlock(before_end, i); + this->FreeBlock(before_end, i); } } - // Free space after the big blocks - for (s32 i{big_index - 1}; i >= 0; i--) { - const std::size_t block_size{blocks[i].GetSize()}; + // Free space after the big blocks. + for (s32 i = big_index - 1; i >= 0; i--) { + const size_t block_size = m_blocks[i].GetSize(); while (after_start + block_size <= after_end) { - FreeBlock(after_start, i); + this->FreeBlock(after_start, i); after_start += block_size; } } } -std::size_t KPageHeap::CalculateManagementOverheadSize(std::size_t region_size) { - std::size_t overhead_size = 0; - for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) { - const std::size_t cur_block_shift{MemoryBlockPageShifts[i]}; - const std::size_t next_block_shift{ - (i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0}; +size_t KPageHeap::CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts, + size_t num_block_shifts) { + size_t overhead_size = 0; + for (size_t i = 0; i < num_block_shifts; i++) { + const size_t cur_block_shift = block_shifts[i]; + const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0; overhead_size += KPageHeap::Block::CalculateManagementOverheadSize( region_size, cur_block_shift, next_block_shift); } diff --git a/src/core/hle/kernel/k_page_heap.h b/src/core/hle/kernel/k_page_heap.h index a65aa28a0..0917a8bed 100644 --- a/src/core/hle/kernel/k_page_heap.h +++ b/src/core/hle/kernel/k_page_heap.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -23,54 +22,73 @@ public: KPageHeap() = default; ~KPageHeap() = default; - constexpr VAddr GetAddress() const { - return heap_address; + constexpr PAddr GetAddress() const { + return m_heap_address; } - constexpr std::size_t GetSize() const { - return heap_size; + constexpr size_t GetSize() const { + return m_heap_size; } - constexpr VAddr GetEndAddress() const { - return GetAddress() + GetSize(); + constexpr PAddr GetEndAddress() const { + return this->GetAddress() + this->GetSize(); } - constexpr std::size_t GetPageOffset(VAddr block) const { - return (block - GetAddress()) / PageSize; + constexpr size_t GetPageOffset(PAddr block) const { + return (block - this->GetAddress()) / PageSize; + } + constexpr size_t GetPageOffsetToEnd(PAddr block) const { + return (this->GetEndAddress() - block) / PageSize; + } + + void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address, + size_t management_size) { + return this->Initialize(heap_address, heap_size, management_address, management_size, + MemoryBlockPageShifts.data(), NumMemoryBlockPageShifts); + } + + size_t GetFreeSize() const { + return this->GetNumFreePages() * PageSize; } - void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size); - VAddr AllocateBlock(s32 index, bool random); - void Free(VAddr addr, std::size_t num_pages); + void SetInitialUsedSize(size_t reserved_size) { + // Check that the reserved size is valid. + const size_t free_size = this->GetNumFreePages() * PageSize; + ASSERT(m_heap_size >= free_size + reserved_size); - void UpdateUsedSize() { - used_size = heap_size - (GetNumFreePages() * PageSize); + // Set the initial used size. + m_initial_used_size = m_heap_size - free_size - reserved_size; } - static std::size_t CalculateManagementOverheadSize(std::size_t region_size); + PAddr AllocateBlock(s32 index, bool random); + void Free(PAddr addr, size_t num_pages); + + static size_t CalculateManagementOverheadSize(size_t region_size) { + return CalculateManagementOverheadSize(region_size, MemoryBlockPageShifts.data(), + NumMemoryBlockPageShifts); + } - static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) { - const auto target_pages{std::max(num_pages, align_pages)}; - for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) { - if (target_pages <= - (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) { + static constexpr s32 GetAlignedBlockIndex(size_t num_pages, size_t align_pages) { + const size_t target_pages = std::max(num_pages, align_pages); + for (size_t i = 0; i < NumMemoryBlockPageShifts; i++) { + if (target_pages <= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) { return static_cast<s32>(i); } } return -1; } - static constexpr s32 GetBlockIndex(std::size_t num_pages) { - for (s32 i{static_cast<s32>(NumMemoryBlockPageShifts) - 1}; i >= 0; i--) { - if (num_pages >= (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) { + static constexpr s32 GetBlockIndex(size_t num_pages) { + for (s32 i = static_cast<s32>(NumMemoryBlockPageShifts) - 1; i >= 0; i--) { + if (num_pages >= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) { return i; } } return -1; } - static constexpr std::size_t GetBlockSize(std::size_t index) { - return static_cast<std::size_t>(1) << MemoryBlockPageShifts[index]; + static constexpr size_t GetBlockSize(size_t index) { + return size_t(1) << MemoryBlockPageShifts[index]; } - static constexpr std::size_t GetBlockNumPages(std::size_t index) { + static constexpr size_t GetBlockNumPages(size_t index) { return GetBlockSize(index) / PageSize; } @@ -83,114 +101,116 @@ private: Block() = default; ~Block() = default; - constexpr std::size_t GetShift() const { - return block_shift; + constexpr size_t GetShift() const { + return m_block_shift; } - constexpr std::size_t GetNextShift() const { - return next_block_shift; + constexpr size_t GetNextShift() const { + return m_next_block_shift; } - constexpr std::size_t GetSize() const { - return static_cast<std::size_t>(1) << GetShift(); + constexpr size_t GetSize() const { + return u64(1) << this->GetShift(); } - constexpr std::size_t GetNumPages() const { - return GetSize() / PageSize; + constexpr size_t GetNumPages() const { + return this->GetSize() / PageSize; } - constexpr std::size_t GetNumFreeBlocks() const { - return bitmap.GetNumBits(); + constexpr size_t GetNumFreeBlocks() const { + return m_bitmap.GetNumBits(); } - constexpr std::size_t GetNumFreePages() const { - return GetNumFreeBlocks() * GetNumPages(); + constexpr size_t GetNumFreePages() const { + return this->GetNumFreeBlocks() * this->GetNumPages(); } - u64* Initialize(VAddr addr, std::size_t size, std::size_t bs, std::size_t nbs, - u64* bit_storage) { - // Set shifts - block_shift = bs; - next_block_shift = nbs; - - // Align up the address - VAddr end{addr + size}; - const auto align{(next_block_shift != 0) ? (1ULL << next_block_shift) - : (1ULL << block_shift)}; - addr = Common::AlignDown((addr), align); - end = Common::AlignUp((end), align); - - heap_address = addr; - end_offset = (end - addr) / (1ULL << block_shift); - return bitmap.Initialize(bit_storage, end_offset); + u64* Initialize(PAddr addr, size_t size, size_t bs, size_t nbs, u64* bit_storage) { + // Set shifts. + m_block_shift = bs; + m_next_block_shift = nbs; + + // Align up the address. + PAddr end = addr + size; + const size_t align = (m_next_block_shift != 0) ? (u64(1) << m_next_block_shift) + : (u64(1) << m_block_shift); + addr = Common::AlignDown(addr, align); + end = Common::AlignUp(end, align); + + m_heap_address = addr; + m_end_offset = (end - addr) / (u64(1) << m_block_shift); + return m_bitmap.Initialize(bit_storage, m_end_offset); } - VAddr PushBlock(VAddr address) { - // Set the bit for the free block - std::size_t offset{(address - heap_address) >> GetShift()}; - bitmap.SetBit(offset); + PAddr PushBlock(PAddr address) { + // Set the bit for the free block. + size_t offset = (address - m_heap_address) >> this->GetShift(); + m_bitmap.SetBit(offset); - // If we have a next shift, try to clear the blocks below and return the address - if (GetNextShift()) { - const auto diff{1ULL << (GetNextShift() - GetShift())}; + // If we have a next shift, try to clear the blocks below this one and return the new + // address. + if (this->GetNextShift()) { + const size_t diff = u64(1) << (this->GetNextShift() - this->GetShift()); offset = Common::AlignDown(offset, diff); - if (bitmap.ClearRange(offset, diff)) { - return heap_address + (offset << GetShift()); + if (m_bitmap.ClearRange(offset, diff)) { + return m_heap_address + (offset << this->GetShift()); } } - // We couldn't coalesce, or we're already as big as possible - return 0; + // We couldn't coalesce, or we're already as big as possible. + return {}; } - VAddr PopBlock(bool random) { - // Find a free block - const s64 soffset{bitmap.FindFreeBlock(random)}; + PAddr PopBlock(bool random) { + // Find a free block. + s64 soffset = m_bitmap.FindFreeBlock(random); if (soffset < 0) { - return 0; + return {}; } - const auto offset{static_cast<std::size_t>(soffset)}; + const size_t offset = static_cast<size_t>(soffset); - // Update our tracking and return it - bitmap.ClearBit(offset); - return heap_address + (offset << GetShift()); + // Update our tracking and return it. + m_bitmap.ClearBit(offset); + return m_heap_address + (offset << this->GetShift()); } - static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size, - std::size_t cur_block_shift, - std::size_t next_block_shift) { - const auto cur_block_size{(1ULL << cur_block_shift)}; - const auto next_block_size{(1ULL << next_block_shift)}; - const auto align{(next_block_shift != 0) ? next_block_size : cur_block_size}; + public: + static constexpr size_t CalculateManagementOverheadSize(size_t region_size, + size_t cur_block_shift, + size_t next_block_shift) { + const size_t cur_block_size = (u64(1) << cur_block_shift); + const size_t next_block_size = (u64(1) << next_block_shift); + const size_t align = (next_block_shift != 0) ? next_block_size : cur_block_size; return KPageBitmap::CalculateManagementOverheadSize( (align * 2 + Common::AlignUp(region_size, align)) / cur_block_size); } private: - KPageBitmap bitmap; - VAddr heap_address{}; - uintptr_t end_offset{}; - std::size_t block_shift{}; - std::size_t next_block_shift{}; + KPageBitmap m_bitmap; + PAddr m_heap_address{}; + uintptr_t m_end_offset{}; + size_t m_block_shift{}; + size_t m_next_block_shift{}; }; - constexpr std::size_t GetNumFreePages() const { - std::size_t num_free{}; - - for (const auto& block : blocks) { - num_free += block.GetNumFreePages(); - } - - return num_free; - } +private: + void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address, + size_t management_size, const size_t* block_shifts, size_t num_block_shifts); + size_t GetNumFreePages() const; - void FreeBlock(VAddr block, s32 index); + void FreeBlock(PAddr block, s32 index); - static constexpr std::size_t NumMemoryBlockPageShifts{7}; - static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{ + static constexpr size_t NumMemoryBlockPageShifts{7}; + static constexpr std::array<size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{ 0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E, }; - VAddr heap_address{}; - std::size_t heap_size{}; - std::size_t used_size{}; - std::array<Block, NumMemoryBlockPageShifts> blocks{}; - std::vector<u64> metadata; +private: + static size_t CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts, + size_t num_block_shifts); + +private: + PAddr m_heap_address{}; + size_t m_heap_size{}; + size_t m_initial_used_size{}; + size_t m_num_blocks{}; + std::array<Block, NumMemoryBlockPageShifts> m_blocks{}; + std::vector<u64> m_management_data; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 912853e5c..d975de844 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/alignment.h" #include "common/assert.h" @@ -10,7 +9,7 @@ #include "core/hle/kernel/k_address_space_info.h" #include "core/hle/kernel/k_memory_block.h" #include "core/hle/kernel/k_memory_block_manager.h" -#include "core/hle/kernel/k_page_linked_list.h" +#include "core/hle/kernel/k_page_group.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_resource_limit.h" @@ -36,29 +35,11 @@ constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceT case FileSys::ProgramAddressSpaceType::Is39Bit: return 39; default: - UNREACHABLE(); + ASSERT(false); return {}; } } -constexpr u64 GetAddressInRange(const KMemoryInfo& info, VAddr addr) { - if (info.GetAddress() < addr) { - return addr; - } - return info.GetAddress(); -} - -constexpr std::size_t GetSizeInRange(const KMemoryInfo& info, VAddr start, VAddr end) { - std::size_t size{info.GetSize()}; - if (info.GetAddress() < start) { - size -= start - info.GetAddress(); - } - if (info.GetEndAddress() > end) { - size -= info.GetEndAddress() - end; - } - return size; -} - } // namespace KPageTable::KPageTable(Core::System& system_) @@ -66,9 +47,9 @@ KPageTable::KPageTable(Core::System& system_) KPageTable::~KPageTable() = default; -ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, - bool enable_aslr, VAddr code_addr, - std::size_t code_size, KMemoryManager::Pool pool) { +Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, + VAddr code_addr, std::size_t code_size, + KMemoryManager::Pool pool) { const auto GetSpaceStart = [this](KAddressSpaceInfo::Type type) { return KAddressSpaceInfo::GetAddressSpaceStart(address_space_width, type); @@ -84,7 +65,6 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_ std::size_t alias_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Alias)}; std::size_t heap_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Heap)}; - ASSERT(start <= code_addr); ASSERT(code_addr < code_addr + code_size); ASSERT(code_addr + code_size - 1 <= end - 1); @@ -147,7 +127,7 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_ const std::size_t needed_size{ (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)}; if (alloc_size < needed_size) { - UNREACHABLE(); + ASSERT(false); return ResultOutOfMemory; } @@ -277,8 +257,8 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_ return InitializeMemoryLayout(start, end); } -ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryState state, - KMemoryPermission perm) { +Result KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryState state, + KMemoryPermission perm) { const u64 size{num_pages * PageSize}; // Validate the mapping request. @@ -291,89 +271,367 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::None, KMemoryAttribute::None)); + KPageGroup pg; + R_TRY(system.Kernel().MemoryManager().AllocateAndOpen( + &pg, num_pages, + KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, allocation_option))); - KPageLinkedList page_linked_list; - R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool, - allocation_option)); - R_TRY(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup)); + R_TRY(Operate(addr, num_pages, pg, OperationType::MapGroup)); block_manager->Update(addr, num_pages, state, perm); return ResultSuccess; } -ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { +Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) { + // Validate the mapping request. + R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), + ResultInvalidMemoryRegion); + + // Lock the table. KScopedLightLock lk(general_lock); - const std::size_t num_pages{size / PageSize}; + // Verify that the source memory is normal heap. + KMemoryState src_state{}; + KMemoryPermission src_perm{}; + std::size_t num_src_allocator_blocks{}; + R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks, + src_address, size, KMemoryState::All, KMemoryState::Normal, + KMemoryPermission::All, KMemoryPermission::UserReadWrite, + KMemoryAttribute::All, KMemoryAttribute::None)); - KMemoryState state{}; - KMemoryPermission perm{}; - CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, nullptr, src_addr, size, - KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All, - KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask, - KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); + // Verify that the destination memory is unmapped. + std::size_t num_dst_allocator_blocks{}; + R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All, + KMemoryState::Free, KMemoryPermission::None, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryAttribute::None)); - if (IsRegionMapped(dst_addr, size)) { - return ResultInvalidCurrentMemory; + // Map the code memory. + { + // Determine the number of pages being operated on. + const std::size_t num_pages = size / PageSize; + + // Create page groups for the memory being mapped. + KPageGroup pg; + AddRegionToPages(src_address, num_pages, pg); + + // Reprotect the source as kernel-read/not mapped. + const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead | + KMemoryPermission::NotMapped); + R_TRY(Operate(src_address, num_pages, new_perm, OperationType::ChangePermissions)); + + // Ensure that we unprotect the source pages on failure. + auto unprot_guard = SCOPE_GUARD({ + ASSERT(this->Operate(src_address, num_pages, src_perm, OperationType::ChangePermissions) + .IsSuccess()); + }); + + // Map the alias pages. + R_TRY(MapPages(dst_address, pg, new_perm)); + + // We successfully mapped the alias pages, so we don't need to unprotect the src pages on + // failure. + unprot_guard.Cancel(); + + // Apply the memory block updates. + block_manager->Update(src_address, num_pages, src_state, new_perm, + KMemoryAttribute::Locked); + block_manager->Update(dst_address, num_pages, KMemoryState::AliasCode, new_perm, + KMemoryAttribute::None); } - KPageLinkedList page_linked_list; - AddRegionToPages(src_addr, num_pages, page_linked_list); + return ResultSuccess; +} + +Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size, + ICacheInvalidationStrategy icache_invalidation_strategy) { + // Validate the mapping request. + R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), + ResultInvalidMemoryRegion); + + // Lock the table. + KScopedLightLock lk(general_lock); + + // Verify that the source memory is locked normal heap. + std::size_t num_src_allocator_blocks{}; + R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size, + KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None, + KMemoryPermission::None, KMemoryAttribute::All, + KMemoryAttribute::Locked)); + + // Verify that the destination memory is aliasable code. + std::size_t num_dst_allocator_blocks{}; + R_TRY(this->CheckMemoryStateContiguous( + std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias, + KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, + KMemoryAttribute::All, KMemoryAttribute::None)); + // Determine whether any pages being unmapped are code. + bool any_code_pages = false; { - auto block_guard = detail::ScopeExit( - [&] { Operate(src_addr, num_pages, perm, OperationType::ChangePermissions); }); + KMemoryBlockManager::const_iterator it = block_manager->FindIterator(dst_address); + while (true) { + // Get the memory info. + const KMemoryInfo info = it->GetMemoryInfo(); + + // Check if the memory has code flag. + if ((info.GetState() & KMemoryState::FlagCode) != KMemoryState::None) { + any_code_pages = true; + break; + } - CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None, - OperationType::ChangePermissions)); - CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::None)); + // Check if we're done. + if (dst_address + size - 1 <= info.GetLastAddress()) { + break; + } - block_guard.Cancel(); + // Advance. + ++it; + } } - block_manager->Update(src_addr, num_pages, state, KMemoryPermission::None, - KMemoryAttribute::Locked); - block_manager->Update(dst_addr, num_pages, KMemoryState::AliasCode); + // Ensure that we maintain the instruction cache. + bool reprotected_pages = false; + SCOPE_EXIT({ + if (reprotected_pages && any_code_pages) { + if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) { + system.InvalidateCpuInstructionCacheRange(dst_address, size); + } else { + system.InvalidateCpuInstructionCaches(); + } + } + }); + + // Unmap. + { + // Determine the number of pages being operated on. + const std::size_t num_pages = size / PageSize; + + // Unmap the aliased copy of the pages. + R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap)); + + // Try to set the permissions for the source pages back to what they should be. + R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, + OperationType::ChangePermissions)); + + // Apply the memory block updates. + block_manager->Update(dst_address, num_pages, KMemoryState::None); + block_manager->Update(src_address, num_pages, KMemoryState::Normal, + KMemoryPermission::UserReadWrite); + + // Note that we reprotected pages. + reprotected_pages = true; + } return ResultSuccess; } -ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { - KScopedLightLock lk(general_lock); +VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages, + std::size_t num_pages, std::size_t alignment, std::size_t offset, + std::size_t guard_pages) { + VAddr address = 0; + + if (num_pages <= region_num_pages) { + if (this->IsAslrEnabled()) { + // Try to directly find a free area up to 8 times. + for (std::size_t i = 0; i < 8; i++) { + const std::size_t random_offset = + KSystemControl::GenerateRandomRange( + 0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) * + alignment; + const VAddr candidate = + Common::AlignDown((region_start + random_offset), alignment) + offset; + + KMemoryInfo info = this->QueryInfoImpl(candidate); + + if (info.state != KMemoryState::Free) { + continue; + } + if (region_start > candidate) { + continue; + } + if (info.GetAddress() + guard_pages * PageSize > candidate) { + continue; + } + + const VAddr candidate_end = candidate + (num_pages + guard_pages) * PageSize - 1; + if (candidate_end > info.GetLastAddress()) { + continue; + } + if (candidate_end > region_start + region_num_pages * PageSize - 1) { + continue; + } + + address = candidate; + break; + } + // Fall back to finding the first free area with a random offset. + if (address == 0) { + // NOTE: Nintendo does not account for guard pages here. + // This may theoretically cause an offset to be chosen that cannot be mapped. We + // will account for guard pages. + const std::size_t offset_pages = KSystemControl::GenerateRandomRange( + 0, region_num_pages - num_pages - guard_pages); + address = block_manager->FindFreeArea(region_start + offset_pages * PageSize, + region_num_pages - offset_pages, num_pages, + alignment, offset, guard_pages); + } + } - if (!size) { - return ResultSuccess; + // Find the first free area. + if (address == 0) { + address = block_manager->FindFreeArea(region_start, region_num_pages, num_pages, + alignment, offset, guard_pages); + } } - const std::size_t num_pages{size / PageSize}; + return address; +} - CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, nullptr, src_addr, size, - KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None, - KMemoryPermission::None, KMemoryAttribute::Mask, - KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); +Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) { + ASSERT(this->IsLockedByCurrentThread()); - KMemoryState state{}; - CASCADE_CODE(CheckMemoryState( - &state, nullptr, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias, - KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, - KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); - CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None, - KMemoryPermission::None, KMemoryAttribute::Mask, - KMemoryAttribute::None)); - CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap)); + const size_t size = num_pages * PageSize; - block_manager->Update(dst_addr, num_pages, KMemoryState::Free); - block_manager->Update(src_addr, num_pages, KMemoryState::Normal, - KMemoryPermission::UserReadWrite); + // We're making a new group, not adding to an existing one. + R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory); + + // Begin traversal. + Common::PageTable::TraversalContext context; + Common::PageTable::TraversalEntry next_entry; + R_UNLESS(page_table_impl.BeginTraversal(next_entry, context, addr), ResultInvalidCurrentMemory); - system.InvalidateCpuInstructionCacheRange(dst_addr, size); + // Prepare tracking variables. + PAddr cur_addr = next_entry.phys_addr; + size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1)); + size_t tot_size = cur_size; + + // Iterate, adding to group as we go. + const auto& memory_layout = system.Kernel().MemoryLayout(); + while (tot_size < size) { + R_UNLESS(page_table_impl.ContinueTraversal(next_entry, context), + ResultInvalidCurrentMemory); + + if (next_entry.phys_addr != (cur_addr + cur_size)) { + const size_t cur_pages = cur_size / PageSize; + + R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory); + R_TRY(pg.AddBlock(cur_addr, cur_pages)); + + cur_addr = next_entry.phys_addr; + cur_size = next_entry.block_size; + } else { + cur_size += next_entry.block_size; + } + + tot_size += next_entry.block_size; + } + + // Ensure we add the right amount for the last block. + if (tot_size > size) { + cur_size -= (tot_size - size); + } + + // Add the last block. + const size_t cur_pages = cur_size / PageSize; + R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory); + R_TRY(pg.AddBlock(cur_addr, cur_pages)); return ResultSuccess; } -ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, - KPageTable& src_page_table, VAddr src_addr) { +bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t num_pages) { + ASSERT(this->IsLockedByCurrentThread()); + + const size_t size = num_pages * PageSize; + const auto& pg = pg_ll.Nodes(); + const auto& memory_layout = system.Kernel().MemoryLayout(); + + // Empty groups are necessarily invalid. + if (pg.empty()) { + return false; + } + + // We're going to validate that the group we'd expect is the group we see. + auto cur_it = pg.begin(); + PAddr cur_block_address = cur_it->GetAddress(); + size_t cur_block_pages = cur_it->GetNumPages(); + + auto UpdateCurrentIterator = [&]() { + if (cur_block_pages == 0) { + if ((++cur_it) == pg.end()) { + return false; + } + + cur_block_address = cur_it->GetAddress(); + cur_block_pages = cur_it->GetNumPages(); + } + return true; + }; + + // Begin traversal. + Common::PageTable::TraversalContext context; + Common::PageTable::TraversalEntry next_entry; + if (!page_table_impl.BeginTraversal(next_entry, context, addr)) { + return false; + } + + // Prepare tracking variables. + PAddr cur_addr = next_entry.phys_addr; + size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1)); + size_t tot_size = cur_size; + + // Iterate, comparing expected to actual. + while (tot_size < size) { + if (!page_table_impl.ContinueTraversal(next_entry, context)) { + return false; + } + + if (next_entry.phys_addr != (cur_addr + cur_size)) { + const size_t cur_pages = cur_size / PageSize; + + if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) { + return false; + } + + if (!UpdateCurrentIterator()) { + return false; + } + + if (cur_block_address != cur_addr || cur_block_pages < cur_pages) { + return false; + } + + cur_block_address += cur_size; + cur_block_pages -= cur_pages; + cur_addr = next_entry.phys_addr; + cur_size = next_entry.block_size; + } else { + cur_size += next_entry.block_size; + } + + tot_size += next_entry.block_size; + } + + // Ensure we compare the right amount for the last block. + if (tot_size > size) { + cur_size -= (tot_size - size); + } + + if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) { + return false; + } + + if (!UpdateCurrentIterator()) { + return false; + } + + return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize); +} + +Result KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, + VAddr src_addr) { KScopedLightLock lk(general_lock); const std::size_t num_pages{size / PageSize}; @@ -397,156 +655,486 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None, KMemoryAttribute::None); + system.InvalidateCpuInstructionCaches(); + return ResultSuccess; } -ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) { +Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { // Lock the physical memory lock. KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); - // Lock the table. - KScopedLightLock lk(general_lock); - - std::size_t mapped_size{}; - const VAddr end_addr{addr + size}; + // Calculate the last address for convenience. + const VAddr last_address = address + size - 1; - block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) { - if (info.state != KMemoryState::Free) { - mapped_size += GetSizeInRange(info, addr, end_addr); - } - }); + // Define iteration variables. + VAddr cur_address; + std::size_t mapped_size; - if (mapped_size == size) { - return ResultSuccess; - } + // The entire mapping process can be retried. + while (true) { + // Check if the memory is already mapped. + { + // Lock the table. + KScopedLightLock lk(general_lock); + + // Iterate over the memory. + cur_address = address; + mapped_size = 0; + + auto it = block_manager->FindIterator(cur_address); + while (true) { + // Check that the iterator is valid. + ASSERT(it != block_manager->end()); + + // Get the memory info. + const KMemoryInfo info = it->GetMemoryInfo(); + + // Check if we're done. + if (last_address <= info.GetLastAddress()) { + if (info.GetState() != KMemoryState::Free) { + mapped_size += (last_address + 1 - cur_address); + } + break; + } + + // Track the memory if it's mapped. + if (info.GetState() != KMemoryState::Free) { + mapped_size += VAddr(info.GetEndAddress()) - cur_address; + } + + // Advance. + cur_address = info.GetEndAddress(); + ++it; + } - const std::size_t remaining_size{size - mapped_size}; - const std::size_t remaining_pages{remaining_size / PageSize}; + // If the size mapped is the size requested, we've nothing to do. + R_SUCCEED_IF(size == mapped_size); + } - // Reserve the memory from the process resource limit. - KScopedResourceReservation memory_reservation( - system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory, - remaining_size); - if (!memory_reservation.Succeeded()) { - LOG_ERROR(Kernel, "Could not reserve remaining {:X} bytes", remaining_size); - return ResultLimitReached; + // Allocate and map the memory. + { + // Reserve the memory from the process resource limit. + KScopedResourceReservation memory_reservation( + system.Kernel().CurrentProcess()->GetResourceLimit(), + LimitableResource::PhysicalMemory, size - mapped_size); + R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); + + // Allocate pages for the new memory. + KPageGroup pg; + R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess( + &pg, (size - mapped_size) / PageSize, + KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0)); + + // Map the memory. + { + // Lock the table. + KScopedLightLock lk(general_lock); + + size_t num_allocator_blocks = 0; + + // Verify that nobody has mapped memory since we first checked. + { + // Iterate over the memory. + size_t checked_mapped_size = 0; + cur_address = address; + + auto it = block_manager->FindIterator(cur_address); + while (true) { + // Check that the iterator is valid. + ASSERT(it != block_manager->end()); + + // Get the memory info. + const KMemoryInfo info = it->GetMemoryInfo(); + + const bool is_free = info.GetState() == KMemoryState::Free; + if (is_free) { + if (info.GetAddress() < address) { + ++num_allocator_blocks; + } + if (last_address < info.GetLastAddress()) { + ++num_allocator_blocks; + } + } + + // Check if we're done. + if (last_address <= info.GetLastAddress()) { + if (!is_free) { + checked_mapped_size += (last_address + 1 - cur_address); + } + break; + } + + // Track the memory if it's mapped. + if (!is_free) { + checked_mapped_size += VAddr(info.GetEndAddress()) - cur_address; + } + + // Advance. + cur_address = info.GetEndAddress(); + ++it; + } + + // If the size now isn't what it was before, somebody mapped or unmapped + // concurrently. If this happened, retry. + if (mapped_size != checked_mapped_size) { + continue; + } + } + + // Reset the current tracking address, and make sure we clean up on failure. + cur_address = address; + auto unmap_guard = detail::ScopeExit([&] { + if (cur_address > address) { + const VAddr last_unmap_address = cur_address - 1; + + // Iterate, unmapping the pages. + cur_address = address; + + auto it = block_manager->FindIterator(cur_address); + while (true) { + // Check that the iterator is valid. + ASSERT(it != block_manager->end()); + + // Get the memory info. + const KMemoryInfo info = it->GetMemoryInfo(); + + // If the memory state is free, we mapped it and need to unmap it. + if (info.GetState() == KMemoryState::Free) { + // Determine the range to unmap. + const size_t cur_pages = + std::min(VAddr(info.GetEndAddress()) - cur_address, + last_unmap_address + 1 - cur_address) / + PageSize; + + // Unmap. + ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, + OperationType::Unmap) + .IsSuccess()); + } + + // Check if we're done. + if (last_unmap_address <= info.GetLastAddress()) { + break; + } + + // Advance. + cur_address = info.GetEndAddress(); + ++it; + } + } + }); + + // Iterate over the memory. + auto pg_it = pg.Nodes().begin(); + PAddr pg_phys_addr = pg_it->GetAddress(); + size_t pg_pages = pg_it->GetNumPages(); + + auto it = block_manager->FindIterator(cur_address); + while (true) { + // Check that the iterator is valid. + ASSERT(it != block_manager->end()); + + // Get the memory info. + const KMemoryInfo info = it->GetMemoryInfo(); + + // If it's unmapped, we need to map it. + if (info.GetState() == KMemoryState::Free) { + // Determine the range to map. + size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address, + last_address + 1 - cur_address) / + PageSize; + + // While we have pages to map, map them. + while (map_pages > 0) { + // Check if we're at the end of the physical block. + if (pg_pages == 0) { + // Ensure there are more pages to map. + ASSERT(pg_it != pg.Nodes().end()); + + // Advance our physical block. + ++pg_it; + pg_phys_addr = pg_it->GetAddress(); + pg_pages = pg_it->GetNumPages(); + } + + // Map whatever we can. + const size_t cur_pages = std::min(pg_pages, map_pages); + R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite, + OperationType::Map, pg_phys_addr)); + + // Advance. + cur_address += cur_pages * PageSize; + map_pages -= cur_pages; + + pg_phys_addr += cur_pages * PageSize; + pg_pages -= cur_pages; + } + } + + // Check if we're done. + if (last_address <= info.GetLastAddress()) { + break; + } + + // Advance. + cur_address = info.GetEndAddress(); + ++it; + } + + // We succeeded, so commit the memory reservation. + memory_reservation.Commit(); + + // Increase our tracked mapped size. + mapped_physical_memory_size += (size - mapped_size); + + // Update the relevant memory blocks. + block_manager->Update(address, size / PageSize, KMemoryState::Free, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryState::Normal, KMemoryPermission::UserReadWrite, + KMemoryAttribute::None); + + // Cancel our guard. + unmap_guard.Cancel(); + + return ResultSuccess; + } + } } +} - KPageLinkedList page_linked_list; +Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { + // Lock the physical memory lock. + KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); - CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages, - memory_pool, allocation_option)); + // Lock the table. + KScopedLightLock lk(general_lock); - // We succeeded, so commit the memory reservation. - memory_reservation.Commit(); + // Calculate the last address for convenience. + const VAddr last_address = address + size - 1; - // Map the memory. - auto node{page_linked_list.Nodes().begin()}; - PAddr map_addr{node->GetAddress()}; - std::size_t src_num_pages{node->GetNumPages()}; - block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) { - if (info.state != KMemoryState::Free) { - return; - } + // Define iteration variables. + VAddr cur_address = 0; + std::size_t mapped_size = 0; + std::size_t num_allocator_blocks = 0; - std::size_t dst_num_pages{GetSizeInRange(info, addr, end_addr) / PageSize}; - VAddr dst_addr{GetAddressInRange(info, addr)}; + // Check if the memory is mapped. + { + // Iterate over the memory. + cur_address = address; + mapped_size = 0; + + auto it = block_manager->FindIterator(cur_address); + while (true) { + // Check that the iterator is valid. + ASSERT(it != block_manager->end()); + + // Get the memory info. + const KMemoryInfo info = it->GetMemoryInfo(); + + // Verify the memory's state. + const bool is_normal = info.GetState() == KMemoryState::Normal && + info.GetAttribute() == KMemoryAttribute::None; + const bool is_free = info.GetState() == KMemoryState::Free; + R_UNLESS(is_normal || is_free, ResultInvalidCurrentMemory); + + if (is_normal) { + R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory); + + if (info.GetAddress() < address) { + ++num_allocator_blocks; + } + if (last_address < info.GetLastAddress()) { + ++num_allocator_blocks; + } + } - while (dst_num_pages) { - if (!src_num_pages) { - node = std::next(node); - map_addr = node->GetAddress(); - src_num_pages = node->GetNumPages(); + // Check if we're done. + if (last_address <= info.GetLastAddress()) { + if (is_normal) { + mapped_size += (last_address + 1 - cur_address); + } + break; } - const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)}; - Operate(dst_addr, num_pages, KMemoryPermission::UserReadWrite, OperationType::Map, - map_addr); + // Track the memory if it's mapped. + if (is_normal) { + mapped_size += VAddr(info.GetEndAddress()) - cur_address; + } - dst_addr += num_pages * PageSize; - map_addr += num_pages * PageSize; - src_num_pages -= num_pages; - dst_num_pages -= num_pages; + // Advance. + cur_address = info.GetEndAddress(); + ++it; } - }); - mapped_physical_memory_size += remaining_size; - - const std::size_t num_pages{size / PageSize}; - block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None, - KMemoryAttribute::None, KMemoryState::Normal, - KMemoryPermission::UserReadWrite, KMemoryAttribute::None); - - return ResultSuccess; -} + // If there's nothing mapped, we've nothing to do. + R_SUCCEED_IF(mapped_size == 0); + } -ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) { - // Lock the physical memory lock. - KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); + // Make a page group for the unmap region. + KPageGroup pg; + { + auto& impl = this->PageTableImpl(); + + // Begin traversal. + Common::PageTable::TraversalContext context; + Common::PageTable::TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0}; + bool cur_valid = false; + Common::PageTable::TraversalEntry next_entry; + bool next_valid = false; + size_t tot_size = 0; + + cur_address = address; + next_valid = impl.BeginTraversal(next_entry, context, cur_address); + next_entry.block_size = + (next_entry.block_size - (next_entry.phys_addr & (next_entry.block_size - 1))); + + // Iterate, building the group. + while (true) { + if ((!next_valid && !cur_valid) || + (next_valid && cur_valid && + next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) { + cur_entry.block_size += next_entry.block_size; + } else { + if (cur_valid) { + // ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr)); + R_TRY(pg.AddBlock(cur_entry.phys_addr, cur_entry.block_size / PageSize)); + } + + // Update tracking variables. + tot_size += cur_entry.block_size; + cur_entry = next_entry; + cur_valid = next_valid; + } - // Lock the table. - KScopedLightLock lk(general_lock); + if (cur_entry.block_size + tot_size >= size) { + break; + } - const VAddr end_addr{addr + size}; - ResultCode result{ResultSuccess}; - std::size_t mapped_size{}; + next_valid = impl.ContinueTraversal(next_entry, context); + } - // Verify that the region can be unmapped - block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) { - if (info.state == KMemoryState::Normal) { - if (info.attribute != KMemoryAttribute::None) { - result = ResultInvalidCurrentMemory; - return; + // Add the last block. + if (cur_valid) { + // ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr)); + R_TRY(pg.AddBlock(cur_entry.phys_addr, (size - tot_size) / PageSize)); + } + } + ASSERT(pg.GetNumPages() == mapped_size / PageSize); + + // Reset the current tracking address, and make sure we clean up on failure. + cur_address = address; + auto remap_guard = detail::ScopeExit([&] { + if (cur_address > address) { + const VAddr last_map_address = cur_address - 1; + cur_address = address; + + // Iterate over the memory we unmapped. + auto it = block_manager->FindIterator(cur_address); + auto pg_it = pg.Nodes().begin(); + PAddr pg_phys_addr = pg_it->GetAddress(); + size_t pg_pages = pg_it->GetNumPages(); + + while (true) { + // Get the memory info for the pages we unmapped, convert to property. + const KMemoryInfo info = it->GetMemoryInfo(); + + // If the memory is normal, we unmapped it and need to re-map it. + if (info.GetState() == KMemoryState::Normal) { + // Determine the range to map. + size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address, + last_map_address + 1 - cur_address) / + PageSize; + + // While we have pages to map, map them. + while (map_pages > 0) { + // Check if we're at the end of the physical block. + if (pg_pages == 0) { + // Ensure there are more pages to map. + ASSERT(pg_it != pg.Nodes().end()); + + // Advance our physical block. + ++pg_it; + pg_phys_addr = pg_it->GetAddress(); + pg_pages = pg_it->GetNumPages(); + } + + // Map whatever we can. + const size_t cur_pages = std::min(pg_pages, map_pages); + ASSERT(this->Operate(cur_address, cur_pages, info.GetPermission(), + OperationType::Map, pg_phys_addr) == ResultSuccess); + + // Advance. + cur_address += cur_pages * PageSize; + map_pages -= cur_pages; + + pg_phys_addr += cur_pages * PageSize; + pg_pages -= cur_pages; + } + } + + // Check if we're done. + if (last_map_address <= info.GetLastAddress()) { + break; + } + + // Advance. + ++it; } - mapped_size += GetSizeInRange(info, addr, end_addr); - } else if (info.state != KMemoryState::Free) { - result = ResultInvalidCurrentMemory; } }); - if (result.IsError()) { - return result; - } - - if (!mapped_size) { - return ResultSuccess; - } + // Iterate over the memory, unmapping as we go. + auto it = block_manager->FindIterator(cur_address); + while (true) { + // Check that the iterator is valid. + ASSERT(it != block_manager->end()); - // Unmap each region within the range - KPageLinkedList page_linked_list; - block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) { - if (info.state == KMemoryState::Normal) { - const std::size_t block_size{GetSizeInRange(info, addr, end_addr)}; - const std::size_t block_num_pages{block_size / PageSize}; - const VAddr block_addr{GetAddressInRange(info, addr)}; + // Get the memory info. + const KMemoryInfo info = it->GetMemoryInfo(); - AddRegionToPages(block_addr, block_size / PageSize, page_linked_list); + // If the memory state is normal, we need to unmap it. + if (info.GetState() == KMemoryState::Normal) { + // Determine the range to unmap. + const size_t cur_pages = std::min(VAddr(info.GetEndAddress()) - cur_address, + last_address + 1 - cur_address) / + PageSize; - if (result = Operate(block_addr, block_num_pages, KMemoryPermission::None, - OperationType::Unmap); - result.IsError()) { - return; - } + // Unmap. + R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap)); } - }); - if (result.IsError()) { - return result; - } - const std::size_t num_pages{size / PageSize}; - system.Kernel().MemoryManager().Free(page_linked_list, num_pages, memory_pool, - allocation_option); + // Check if we're done. + if (last_address <= info.GetLastAddress()) { + break; + } - block_manager->Update(addr, num_pages, KMemoryState::Free); + // Advance. + cur_address = info.GetEndAddress(); + ++it; + } + // Release the memory resource. + mapped_physical_memory_size -= mapped_size; auto process{system.Kernel().CurrentProcess()}; process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size); - mapped_physical_memory_size -= mapped_size; + + // Update memory blocks. + block_manager->Update(address, size / PageSize, KMemoryState::Free, KMemoryPermission::None, + KMemoryAttribute::None); + + // TODO(bunnei): This is a workaround until the next set of changes, where we add reference + // counting for mapped pages. Until then, we must manually close the reference to the page + // group. + system.Kernel().MemoryManager().Close(pg); + + // We succeeded. + remap_guard.Cancel(); return ResultSuccess; } -ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { +Result KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { KScopedLightLock lk(general_lock); KMemoryState src_state{}; @@ -559,7 +1147,7 @@ ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t siz return ResultInvalidCurrentMemory; } - KPageLinkedList page_linked_list; + KPageGroup page_linked_list; const std::size_t num_pages{size / PageSize}; AddRegionToPages(src_addr, num_pages, page_linked_list); @@ -585,7 +1173,7 @@ ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t siz return ResultSuccess; } -ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { +Result KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { KScopedLightLock lk(general_lock); KMemoryState src_state{}; @@ -600,8 +1188,8 @@ ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t s KMemoryPermission::None, KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); - KPageLinkedList src_pages; - KPageLinkedList dst_pages; + KPageGroup src_pages; + KPageGroup dst_pages; const std::size_t num_pages{size / PageSize}; AddRegionToPages(src_addr, num_pages, src_pages); @@ -627,8 +1215,8 @@ ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t s return ResultSuccess; } -ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_list, - KMemoryPermission perm) { +Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list, + KMemoryPermission perm) { ASSERT(this->IsLockedByCurrentThread()); VAddr cur_addr{addr}; @@ -651,8 +1239,8 @@ ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_l return ResultSuccess; } -ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list, - KMemoryState state, KMemoryPermission perm) { +Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state, + KMemoryPermission perm) { // Check that the map is in range. const std::size_t num_pages{page_linked_list.GetNumPages()}; const std::size_t size{num_pages * PageSize}; @@ -675,15 +1263,54 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list return ResultSuccess; } -ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) { +Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, + PAddr phys_addr, bool is_pa_valid, VAddr region_start, + std::size_t region_num_pages, KMemoryState state, + KMemoryPermission perm) { + ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize); + + // Ensure this is a valid map request. + R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state), + ResultInvalidCurrentMemory); + R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory); + + // Lock the table. + KScopedLightLock lk(general_lock); + + // Find a random address to map at. + VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, + this->GetNumGuardPages()); + R_UNLESS(addr != 0, ResultOutOfMemory); + ASSERT(Common::IsAligned(addr, alignment)); + ASSERT(this->CanContain(addr, num_pages * PageSize, state)); + ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, + KMemoryPermission::None, KMemoryPermission::None, + KMemoryAttribute::None, KMemoryAttribute::None) + .IsSuccess()); + + // Perform mapping operation. + if (is_pa_valid) { + R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr)); + } else { + UNIMPLEMENTED(); + } + + // Update the blocks. + block_manager->Update(addr, num_pages, state, perm); + + // We successfully mapped the pages. + *out_addr = addr; + return ResultSuccess; +} + +Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) { ASSERT(this->IsLockedByCurrentThread()); VAddr cur_addr{addr}; for (const auto& node : page_linked_list.Nodes()) { - const std::size_t num_pages{(addr - cur_addr) / PageSize}; - if (const auto result{ - Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)}; + if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None, + OperationType::Unmap)}; result.IsError()) { return result; } @@ -694,8 +1321,7 @@ ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked return ResultSuccess; } -ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, - KMemoryState state) { +Result KPageTable::UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state) { // Check that the unmap is in range. const std::size_t num_pages{page_linked_list.GetNumPages()}; const std::size_t size{num_pages * PageSize}; @@ -718,8 +1344,57 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, return ResultSuccess; } -ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, - Svc::MemoryPermission svc_perm) { +Result KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state) { + // Check that the unmap is in range. + const std::size_t size = num_pages * PageSize; + R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); + + // Lock the table. + KScopedLightLock lk(general_lock); + + // Check the memory state. + std::size_t num_allocator_blocks{}; + R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, + KMemoryState::All, state, KMemoryPermission::None, + KMemoryPermission::None, KMemoryAttribute::All, + KMemoryAttribute::None)); + + // Perform the unmap. + R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap)); + + // Update the blocks. + block_manager->Update(address, num_pages, KMemoryState::Free, KMemoryPermission::None); + + return ResultSuccess; +} + +Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages, + KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr) { + // Ensure that the page group isn't null. + ASSERT(out != nullptr); + + // Make sure that the region we're mapping is valid for the table. + const size_t size = num_pages * PageSize; + R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); + + // Lock the table. + KScopedLightLock lk(general_lock); + + // Check if state allows us to create the group. + R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted, + state | KMemoryState::FlagReferenceCounted, perm_mask, perm, + attr_mask, attr)); + + // Create a new page group for the region. + R_TRY(this->MakePageGroup(*out, address, num_pages)); + + return ResultSuccess; +} + +Result KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, + Svc::MemoryPermission svc_perm) { const size_t num_pages = size / PageSize; // Lock the table. @@ -753,7 +1428,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, new_state = KMemoryState::AliasCodeData; break; default: - UNREACHABLE(); + ASSERT(false); } } @@ -791,7 +1466,7 @@ KMemoryInfo KPageTable::QueryInfo(VAddr addr) { return QueryInfoImpl(addr); } -ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) { +Result KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) { KScopedLightLock lk(general_lock); KMemoryState state{}; @@ -809,7 +1484,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo return ResultSuccess; } -ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) { +Result KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) { KScopedLightLock lk(general_lock); KMemoryState state{}; @@ -824,8 +1499,8 @@ ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) { return ResultSuccess; } -ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size, - Svc::MemoryPermission svc_perm) { +Result KPageTable::SetMemoryPermission(VAddr addr, std::size_t size, + Svc::MemoryPermission svc_perm) { const size_t num_pages = size / PageSize; // Lock the table. @@ -852,7 +1527,7 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size, return ResultSuccess; } -ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr) { +Result KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr) { const size_t num_pages = size / PageSize; ASSERT((static_cast<KMemoryAttribute>(mask) | KMemoryAttribute::SetMask) == KMemoryAttribute::SetMask); @@ -887,7 +1562,7 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask return ResultSuccess; } -ResultCode KPageTable::SetMaxHeapSize(std::size_t size) { +Result KPageTable::SetMaxHeapSize(std::size_t size) { // Lock the table. KScopedLightLock lk(general_lock); @@ -899,7 +1574,7 @@ ResultCode KPageTable::SetMaxHeapSize(std::size_t size) { return ResultSuccess; } -ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) { +Result KPageTable::SetHeapSize(VAddr* out, std::size_t size) { // Lock the physical memory mutex. KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); @@ -966,9 +1641,16 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) { R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Allocate pages for the heap extension. - KPageLinkedList page_linked_list; - R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize, - memory_pool, allocation_option)); + KPageGroup pg; + R_TRY(system.Kernel().MemoryManager().AllocateAndOpen( + &pg, allocation_size / PageSize, + KMemoryManager::EncodeOption(memory_pool, allocation_option))); + + // Clear all the newly allocated pages. + for (const auto& it : pg.Nodes()) { + std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value, + it.GetSize()); + } // Map the pages. { @@ -987,7 +1669,7 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) { // Map the pages. const auto num_pages = allocation_size / PageSize; - R_TRY(Operate(current_heap_end, num_pages, page_linked_list, OperationType::MapGroup)); + R_TRY(Operate(current_heap_end, num_pages, pg, OperationType::MapGroup)); // Clear all the newly allocated pages. for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) { @@ -1034,9 +1716,10 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, if (is_map_only) { R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); } else { - KPageLinkedList page_group; - R_TRY(system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, memory_pool, - allocation_option)); + KPageGroup page_group; + R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess( + &page_group, needed_num_pages, + KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0)); R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup)); } @@ -1045,11 +1728,11 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, return addr; } -ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { +Result KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { KScopedLightLock lk(general_lock); KMemoryPermission perm{}; - if (const ResultCode result{CheckMemoryState( + if (const Result result{CheckMemoryState( nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute, KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None, @@ -1068,11 +1751,11 @@ ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { return ResultSuccess; } -ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) { +Result KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) { KScopedLightLock lk(general_lock); KMemoryPermission perm{}; - if (const ResultCode result{CheckMemoryState( + if (const Result result{CheckMemoryState( nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute, KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None, @@ -1091,61 +1774,24 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) return ResultSuccess; } -ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { - KScopedLightLock lk(general_lock); - - KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite; - - KMemoryPermission old_perm{}; - - if (const ResultCode result{CheckMemoryState( - nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, - KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, - KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)}; - result.IsError()) { - return result; - } - - new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; - - block_manager->UpdateLock( - addr, size / PageSize, - [](KMemoryBlockManager::iterator block, KMemoryPermission permission) { - block->ShareToDevice(permission); - }, - new_perm); - - return ResultSuccess; +Result KPageTable::LockForCodeMemory(KPageGroup* out, VAddr addr, std::size_t size) { + return this->LockMemoryAndOpen( + out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, + KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All, + KMemoryAttribute::None, + static_cast<KMemoryPermission>(KMemoryPermission::NotMapped | + KMemoryPermission::KernelReadWrite), + KMemoryAttribute::Locked); } -ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { - KScopedLightLock lk(general_lock); - - KMemoryPermission new_perm = KMemoryPermission::UserReadWrite; - - KMemoryPermission old_perm{}; - - if (const ResultCode result{CheckMemoryState( - nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, - KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None, - KMemoryAttribute::All, KMemoryAttribute::Locked)}; - result.IsError()) { - return result; - } - - new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; - - block_manager->UpdateLock( - addr, size / PageSize, - [](KMemoryBlockManager::iterator block, KMemoryPermission permission) { - block->UnshareToDevice(permission); - }, - new_perm); - - return ResultSuccess; +Result KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageGroup& pg) { + return this->UnlockMemory( + addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, + KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All, + KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg); } -ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { +Result KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { block_manager = std::make_unique<KMemoryBlockManager>(start, end); return ResultSuccess; @@ -1170,13 +1816,11 @@ bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const { } void KPageTable::AddRegionToPages(VAddr start, std::size_t num_pages, - KPageLinkedList& page_linked_list) { + KPageGroup& page_linked_list) { VAddr addr{start}; while (addr < start + (num_pages * PageSize)) { const PAddr paddr{GetPhysicalAddr(addr)}; - if (!paddr) { - UNREACHABLE(); - } + ASSERT(paddr != 0); page_linked_list.AddBlock(paddr, 1); addr += PageSize; } @@ -1191,8 +1835,8 @@ VAddr KPageTable::AllocateVirtualMemory(VAddr start, std::size_t region_num_page IsKernel() ? 1 : 4); } -ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLinkedList& page_group, - OperationType operation) { +Result KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageGroup& page_group, + OperationType operation) { ASSERT(this->IsLockedByCurrentThread()); ASSERT(Common::IsAligned(addr, PageSize)); @@ -1207,7 +1851,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLin system.Memory().MapMemoryRegion(page_table_impl, addr, size, node.GetAddress()); break; default: - UNREACHABLE(); + ASSERT(false); } addr += size; @@ -1216,8 +1860,8 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLin return ResultSuccess; } -ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, - OperationType operation, PAddr map_addr) { +Result KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, + OperationType operation, PAddr map_addr) { ASSERT(this->IsLockedByCurrentThread()); ASSERT(num_pages > 0); @@ -1238,12 +1882,12 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermiss case OperationType::ChangePermissionsAndRefresh: break; default: - UNREACHABLE(); + ASSERT(false); } return ResultSuccess; } -constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const { +VAddr KPageTable::GetRegionAddress(KMemoryState state) const { switch (state) { case KMemoryState::Free: case KMemoryState::Kernel: @@ -1275,11 +1919,10 @@ constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const { return code_region_start; default: UNREACHABLE(); - return {}; } } -constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const { +std::size_t KPageTable::GetRegionSize(KMemoryState state) const { switch (state) { case KMemoryState::Free: case KMemoryState::Kernel: @@ -1311,7 +1954,6 @@ constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const { return code_region_end - code_region_start; default: UNREACHABLE(); - return {}; } } @@ -1361,10 +2003,10 @@ bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) co } } -ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, KMemoryAttribute attr_mask, - KMemoryAttribute attr) const { +Result KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, + KMemoryState state, KMemoryPermission perm_mask, + KMemoryPermission perm, KMemoryAttribute attr_mask, + KMemoryAttribute attr) const { // Validate the states match expectation. R_UNLESS((info.state & state_mask) == state, ResultInvalidCurrentMemory); R_UNLESS((info.perm & perm_mask) == perm, ResultInvalidCurrentMemory); @@ -1373,12 +2015,11 @@ ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState st return ResultSuccess; } -ResultCode KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, - std::size_t size, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, - KMemoryAttribute attr_mask, - KMemoryAttribute attr) const { +Result KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, + std::size_t size, KMemoryState state_mask, + KMemoryState state, KMemoryPermission perm_mask, + KMemoryPermission perm, KMemoryAttribute attr_mask, + KMemoryAttribute attr) const { ASSERT(this->IsLockedByCurrentThread()); // Get information about the first block. @@ -1416,12 +2057,12 @@ ResultCode KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed return ResultSuccess; } -ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, - KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, - VAddr addr, std::size_t size, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, KMemoryAttribute attr_mask, - KMemoryAttribute attr, KMemoryAttribute ignore_attr) const { +Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, + KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, + VAddr addr, std::size_t size, KMemoryState state_mask, + KMemoryState state, KMemoryPermission perm_mask, + KMemoryPermission perm, KMemoryAttribute attr_mask, + KMemoryAttribute attr, KMemoryAttribute ignore_attr) const { ASSERT(this->IsLockedByCurrentThread()); // Get information about the first block. @@ -1478,4 +2119,109 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi return ResultSuccess; } +Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size, + KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr, + KMemoryPermission new_perm, KMemoryAttribute lock_attr) { + // Validate basic preconditions. + ASSERT((lock_attr & attr) == KMemoryAttribute::None); + ASSERT((lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) == + KMemoryAttribute::None); + + // Validate the lock request. + const size_t num_pages = size / PageSize; + R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); + + // Lock the table. + KScopedLightLock lk(general_lock); + + // Check that the output page group is empty, if it exists. + if (out_pg) { + ASSERT(out_pg->GetNumPages() == 0); + } + + // Check the state. + KMemoryState old_state{}; + KMemoryPermission old_perm{}; + KMemoryAttribute old_attr{}; + size_t num_allocator_blocks{}; + R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), + std::addressof(old_attr), std::addressof(num_allocator_blocks), + addr, size, state_mask | KMemoryState::FlagReferenceCounted, + state | KMemoryState::FlagReferenceCounted, perm_mask, perm, + attr_mask, attr)); + + // Get the physical address, if we're supposed to. + if (out_paddr != nullptr) { + ASSERT(this->GetPhysicalAddressLocked(out_paddr, addr)); + } + + // Make the page group, if we're supposed to. + if (out_pg != nullptr) { + R_TRY(this->MakePageGroup(*out_pg, addr, num_pages)); + } + + // Decide on new perm and attr. + new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; + KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr | lock_attr); + + // Update permission, if we need to. + if (new_perm != old_perm) { + R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions)); + } + + // Apply the memory block updates. + block_manager->Update(addr, num_pages, old_state, new_perm, new_attr); + + return ResultSuccess; +} + +Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, + KMemoryState state, KMemoryPermission perm_mask, + KMemoryPermission perm, KMemoryAttribute attr_mask, + KMemoryAttribute attr, KMemoryPermission new_perm, + KMemoryAttribute lock_attr, const KPageGroup* pg) { + // Validate basic preconditions. + ASSERT((attr_mask & lock_attr) == lock_attr); + ASSERT((attr & lock_attr) == lock_attr); + + // Validate the unlock request. + const size_t num_pages = size / PageSize; + R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); + + // Lock the table. + KScopedLightLock lk(general_lock); + + // Check the state. + KMemoryState old_state{}; + KMemoryPermission old_perm{}; + KMemoryAttribute old_attr{}; + size_t num_allocator_blocks{}; + R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), + std::addressof(old_attr), std::addressof(num_allocator_blocks), + addr, size, state_mask | KMemoryState::FlagReferenceCounted, + state | KMemoryState::FlagReferenceCounted, perm_mask, perm, + attr_mask, attr)); + + // Check the page group. + if (pg != nullptr) { + R_UNLESS(this->IsValidPageGroup(*pg, addr, num_pages), ResultInvalidMemoryRegion); + } + + // Decide on new perm and attr. + new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; + KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr & ~lock_attr); + + // Update permission, if we need to. + if (new_perm != old_perm) { + R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions)); + } + + // Apply the memory block updates. + block_manager->Update(addr, num_pages, old_state, new_perm, new_attr); + + return ResultSuccess; +} + } // namespace Kernel diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index c98887d34..25774f232 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -12,6 +11,7 @@ #include "core/file_sys/program_metadata.h" #include "core/hle/kernel/k_light_lock.h" #include "core/hle/kernel/k_memory_block.h" +#include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_manager.h" #include "core/hle/result.h" @@ -25,45 +25,57 @@ class KMemoryBlockManager; class KPageTable final { public: + enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll }; + YUZU_NON_COPYABLE(KPageTable); YUZU_NON_MOVEABLE(KPageTable); explicit KPageTable(Core::System& system_); ~KPageTable(); - ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, - VAddr code_addr, std::size_t code_size, - KMemoryManager::Pool pool); - ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, - KMemoryPermission perm); - ResultCode MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); - ResultCode UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); - ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, - VAddr src_addr); - ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); - ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size); - ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); - ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); - ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, - KMemoryPermission perm); - ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state); - ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, - Svc::MemoryPermission svc_perm); + Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, + VAddr code_addr, std::size_t code_size, KMemoryManager::Pool pool); + Result MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, + KMemoryPermission perm); + Result MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); + Result UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size, + ICacheInvalidationStrategy icache_invalidation_strategy); + Result UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, + VAddr src_addr); + Result MapPhysicalMemory(VAddr addr, std::size_t size); + Result UnmapPhysicalMemory(VAddr addr, std::size_t size); + Result MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); + Result UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); + Result MapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state, + KMemoryPermission perm); + Result MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, PAddr phys_addr, + KMemoryState state, KMemoryPermission perm) { + return this->MapPages(out_addr, num_pages, alignment, phys_addr, true, + this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize, + state, perm); + } + Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state); + Result UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state); + Result SetProcessMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission svc_perm); KMemoryInfo QueryInfo(VAddr addr); - ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm); - ResultCode ResetTransferMemory(VAddr addr, std::size_t size); - ResultCode SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm); - ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr); - ResultCode SetMaxHeapSize(std::size_t size); - ResultCode SetHeapSize(VAddr* out, std::size_t size); + Result ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm); + Result ResetTransferMemory(VAddr addr, std::size_t size); + Result SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm); + Result SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr); + Result SetMaxHeapSize(std::size_t size); + Result SetHeapSize(VAddr* out, std::size_t size); ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align, bool is_map_only, VAddr region_start, std::size_t region_num_pages, KMemoryState state, KMemoryPermission perm, PAddr map_addr = 0); - ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size); - ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); - ResultCode LockForCodeMemory(VAddr addr, std::size_t size); - ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size); + Result LockForDeviceAddressSpace(VAddr addr, std::size_t size); + Result UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); + Result LockForCodeMemory(KPageGroup* out, VAddr addr, std::size_t size); + Result UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageGroup& pg); + Result MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages, + KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr); Common::PageTable& PageTableImpl() { return page_table_impl; @@ -88,68 +100,97 @@ private: KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared; - ResultCode InitializeMemoryLayout(VAddr start, VAddr end); - ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list, - KMemoryPermission perm); - ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list); + Result InitializeMemoryLayout(VAddr start, VAddr end); + Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm); + Result MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, PAddr phys_addr, + bool is_pa_valid, VAddr region_start, std::size_t region_num_pages, + KMemoryState state, KMemoryPermission perm); + Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list); bool IsRegionMapped(VAddr address, u64 size); bool IsRegionContiguous(VAddr addr, u64 size) const; - void AddRegionToPages(VAddr start, std::size_t num_pages, KPageLinkedList& page_linked_list); + void AddRegionToPages(VAddr start, std::size_t num_pages, KPageGroup& page_linked_list); KMemoryInfo QueryInfoImpl(VAddr addr); VAddr AllocateVirtualMemory(VAddr start, std::size_t region_num_pages, u64 needed_num_pages, std::size_t align); - ResultCode Operate(VAddr addr, std::size_t num_pages, const KPageLinkedList& page_group, - OperationType operation); - ResultCode Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, - OperationType operation, PAddr map_addr = 0); - constexpr VAddr GetRegionAddress(KMemoryState state) const; - constexpr std::size_t GetRegionSize(KMemoryState state) const; - - ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, - std::size_t size, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, KMemoryAttribute attr_mask, - KMemoryAttribute attr) const; - ResultCode CheckMemoryStateContiguous(VAddr addr, std::size_t size, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, KMemoryAttribute attr_mask, - KMemoryAttribute attr) const { + Result Operate(VAddr addr, std::size_t num_pages, const KPageGroup& page_group, + OperationType operation); + Result Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, + OperationType operation, PAddr map_addr = 0); + VAddr GetRegionAddress(KMemoryState state) const; + std::size_t GetRegionSize(KMemoryState state) const; + + VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages, + std::size_t alignment, std::size_t offset, std::size_t guard_pages); + + Result CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, std::size_t size, + KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr) const; + Result CheckMemoryStateContiguous(VAddr addr, std::size_t size, KMemoryState state_mask, + KMemoryState state, KMemoryPermission perm_mask, + KMemoryPermission perm, KMemoryAttribute attr_mask, + KMemoryAttribute attr) const { return this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask, perm, attr_mask, attr); } - ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, KMemoryAttribute attr_mask, - KMemoryAttribute attr) const; - ResultCode CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, - KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, - VAddr addr, std::size_t size, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, KMemoryAttribute attr_mask, - KMemoryAttribute attr, - KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const; - ResultCode CheckMemoryState(std::size_t* out_blocks_needed, VAddr addr, std::size_t size, - KMemoryState state_mask, KMemoryState state, - KMemoryPermission perm_mask, KMemoryPermission perm, - KMemoryAttribute attr_mask, KMemoryAttribute attr, - KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { + Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr) const; + Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, + KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, VAddr addr, + std::size_t size, KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr, + KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const; + Result CheckMemoryState(std::size_t* out_blocks_needed, VAddr addr, std::size_t size, + KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr, + KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr); } - ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, KMemoryAttribute attr_mask, - KMemoryAttribute attr, - KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { + Result CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask, + KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr, + KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { return this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr); } + Result LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size, + KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr, + KMemoryPermission new_perm, KMemoryAttribute lock_attr); + Result UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr, + KMemoryPermission new_perm, KMemoryAttribute lock_attr, + const KPageGroup* pg); + + Result MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages); + bool IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_pages); + bool IsLockedByCurrentThread() const { return general_lock.IsLockedByCurrentThread(); } + bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) { + ASSERT(this->IsLockedByCurrentThread()); + + return layout.IsHeapPhysicalAddress(cached_physical_heap_region, phys_addr); + } + + bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const { + ASSERT(this->IsLockedByCurrentThread()); + + *out = GetPhysicalAddr(virt_addr); + + return *out != 0; + } + mutable KLightLock general_lock; mutable KLightLock map_physical_memory_lock; @@ -210,7 +251,7 @@ public: constexpr VAddr GetAliasCodeRegionSize() const { return alias_code_region_end - alias_code_region_start; } - size_t GetNormalMemorySize() { + std::size_t GetNormalMemorySize() { KScopedLightLock lk(general_lock); return GetHeapSize() + mapped_physical_memory_size; } @@ -253,9 +294,10 @@ public: constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const { return !IsOutsideASLRRegion(address, size); } - - PAddr GetPhysicalAddr(VAddr addr) { - ASSERT(IsLockedByCurrentThread()); + constexpr std::size_t GetNumGuardPages() const { + return IsKernel() ? 1 : 4; + } + PAddr GetPhysicalAddr(VAddr addr) const { const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits]; ASSERT(backing_addr); return backing_addr + addr; @@ -276,10 +318,6 @@ private: return is_aslr_enabled; } - constexpr std::size_t GetNumGuardPages() const { - return IsKernel() ? 1 : 4; - } - constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const { return (address_space_start <= addr) && (num_pages <= (address_space_end - address_space_start) / PageSize) && @@ -311,6 +349,9 @@ private: bool is_kernel{}; bool is_aslr_enabled{}; + u32 heap_fill_value{}; + const KMemoryRegion* cached_physical_heap_region{}; + KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront}; diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp index a8ba09c4a..7a5a9dc2a 100644 --- a/src/core/hle/kernel/k_port.cpp +++ b/src/core/hle/kernel/k_port.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_port.h" @@ -51,13 +50,18 @@ bool KPort::IsServerClosed() const { return state == State::ServerClosed; } -ResultCode KPort::EnqueueSession(KServerSession* session) { +Result KPort::EnqueueSession(KServerSession* session) { KScopedSchedulerLock sl{kernel}; R_UNLESS(state == State::Normal, ResultPortClosed); server.EnqueueSession(session); - server.GetSessionRequestHandler()->ClientConnected(server.AcceptSession()); + + if (auto session_ptr = server.GetSessionRequestHandler().lock()) { + session_ptr->ClientConnected(server.AcceptSession()); + } else { + ASSERT(false); + } return ResultSuccess; } diff --git a/src/core/hle/kernel/k_port.h b/src/core/hle/kernel/k_port.h index b6e4a1fcd..0cfc16dab 100644 --- a/src/core/hle/kernel/k_port.h +++ b/src/core/hle/kernel/k_port.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -35,7 +34,7 @@ public: bool IsServerClosed() const; - ResultCode EnqueueSession(KServerSession* session); + Result EnqueueSession(KServerSession* session); KClientPort& GetClientPort() { return client; diff --git a/src/core/hle/kernel/k_priority_queue.h b/src/core/hle/kernel/k_priority_queue.h index bd779739d..cb2512b0b 100644 --- a/src/core/hle/kernel/k_priority_queue.h +++ b/src/core/hle/kernel/k_priority_queue.h @@ -1,9 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 85c506979..d3e99665f 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -1,6 +1,5 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2015 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <bitset> @@ -13,7 +12,6 @@ #include "common/scope_exit.h" #include "common/settings.h" #include "core/core.h" -#include "core/device_memory.h" #include "core/file_sys/program_metadata.h" #include "core/hle/kernel/code_set.h" #include "core/hle/kernel/k_memory_block_manager.h" @@ -24,7 +22,6 @@ #include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_shared_memory_info.h" -#include "core/hle/kernel/k_slab_heap.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/svc_results.h" @@ -59,76 +56,22 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority thread->GetContext64().cpu_registers[0] = 0; thread->GetContext32().cpu_registers[1] = thread_handle; thread->GetContext64().cpu_registers[1] = thread_handle; - thread->DisableDispatch(); - auto& kernel = system.Kernel(); - // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires - { - KScopedSchedulerLock lock{kernel}; - thread->SetState(ThreadState::Runnable); + if (system.DebuggerEnabled()) { + thread->RequestSuspend(SuspendType::Debug); } + + // Run our thread. + void(thread->Run()); } } // Anonymous namespace -// Represents a page used for thread-local storage. -// -// Each TLS page contains slots that may be used by processes and threads. -// Every process and thread is created with a slot in some arbitrary page -// (whichever page happens to have an available slot). -class TLSPage { -public: - static constexpr std::size_t num_slot_entries = - Core::Memory::PAGE_SIZE / Core::Memory::TLS_ENTRY_SIZE; - - explicit TLSPage(VAddr address) : base_address{address} {} - - bool HasAvailableSlots() const { - return !is_slot_used.all(); - } - - VAddr GetBaseAddress() const { - return base_address; - } - - std::optional<VAddr> ReserveSlot() { - for (std::size_t i = 0; i < is_slot_used.size(); i++) { - if (is_slot_used[i]) { - continue; - } - - is_slot_used[i] = true; - return base_address + (i * Core::Memory::TLS_ENTRY_SIZE); - } - - return std::nullopt; - } - - void ReleaseSlot(VAddr address) { - // Ensure that all given addresses are consistent with how TLS pages - // are intended to be used when releasing slots. - ASSERT(IsWithinPage(address)); - ASSERT((address % Core::Memory::TLS_ENTRY_SIZE) == 0); - - const std::size_t index = (address - base_address) / Core::Memory::TLS_ENTRY_SIZE; - is_slot_used[index] = false; - } - -private: - bool IsWithinPage(VAddr address) const { - return base_address <= address && address < base_address + Core::Memory::PAGE_SIZE; - } - - VAddr base_address; - std::bitset<num_slot_entries> is_slot_used; -}; - -ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name, - ProcessType type) { +Result KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name, + ProcessType type, KResourceLimit* res_limit) { auto& kernel = system.Kernel(); process->name = std::move(process_name); - - process->resource_limit = kernel.GetSystemResourceLimit(); + process->resource_limit = res_limit; process->status = ProcessStatus::Created; process->program_id = 0; process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID() @@ -143,9 +86,6 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st kernel.AppendNewProcess(process); - // Open a reference to the resource limit. - process->resource_limit->Open(); - // Clear remaining fields. process->num_running_threads = 0; process->is_signaled = false; @@ -153,6 +93,9 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st process->is_suspended = false; process->schedule_count = 0; + // Open a reference to the resource limit. + process->resource_limit->Open(); + return ResultSuccess; } @@ -217,7 +160,7 @@ bool KProcess::ReleaseUserException(KThread* thread) { std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(exception_thread))); next != nullptr) { - next->SetState(ThreadState::Runnable); + next->EndWait(ResultSuccess); } KScheduler::SetSchedulerUpdateNeeded(kernel); @@ -232,7 +175,8 @@ void KProcess::PinCurrentThread(s32 core_id) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); // Get the current thread. - KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread(); + KThread* cur_thread = + kernel.Scheduler(static_cast<std::size_t>(core_id)).GetSchedulerCurrentThread(); // If the thread isn't terminated, pin it. if (!cur_thread->IsTerminationRequested()) { @@ -249,7 +193,8 @@ void KProcess::UnpinCurrentThread(s32 core_id) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); // Get the current thread. - KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread(); + KThread* cur_thread = + kernel.Scheduler(static_cast<std::size_t>(core_id)).GetSchedulerCurrentThread(); // Unpin it. cur_thread->Unpin(); @@ -273,8 +218,8 @@ void KProcess::UnpinThread(KThread* thread) { KScheduler::SetSchedulerUpdateNeeded(kernel); } -ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address, - [[maybe_unused]] size_t size) { +Result KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address, + [[maybe_unused]] size_t size) { // Lock ourselves, to prevent concurrent access. KScopedLightLock lk(state_lock); @@ -326,15 +271,19 @@ void KProcess::RemoveSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr a shmem->Close(); } -void KProcess::RegisterThread(const KThread* thread) { +void KProcess::RegisterThread(KThread* thread) { + KScopedLightLock lk{list_lock}; + thread_list.push_back(thread); } -void KProcess::UnregisterThread(const KThread* thread) { +void KProcess::UnregisterThread(KThread* thread) { + KScopedLightLock lk{list_lock}; + thread_list.remove(thread); } -ResultCode KProcess::Reset() { +Result KProcess::Reset() { // Lock the process and the scheduler. KScopedLightLock lk(state_lock); KScopedSchedulerLock sl{kernel}; @@ -348,8 +297,51 @@ ResultCode KProcess::Reset() { return ResultSuccess; } -ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, - std::size_t code_size) { +Result KProcess::SetActivity(ProcessActivity activity) { + // Lock ourselves and the scheduler. + KScopedLightLock lk{state_lock}; + KScopedLightLock list_lk{list_lock}; + KScopedSchedulerLock sl{kernel}; + + // Validate our state. + R_UNLESS(status != ProcessStatus::Exiting, ResultInvalidState); + R_UNLESS(status != ProcessStatus::Exited, ResultInvalidState); + + // Either pause or resume. + if (activity == ProcessActivity::Paused) { + // Verify that we're not suspended. + if (is_suspended) { + return ResultInvalidState; + } + + // Suspend all threads. + for (auto* thread : GetThreadList()) { + thread->RequestSuspend(SuspendType::Process); + } + + // Set ourselves as suspended. + SetSuspended(true); + } else { + ASSERT(activity == ProcessActivity::Runnable); + + // Verify that we're suspended. + if (!is_suspended) { + return ResultInvalidState; + } + + // Resume all threads. + for (auto* thread : GetThreadList()) { + thread->Resume(SuspendType::Process); + } + + // Set ourselves as resumed. + SetSuspended(false); + } + + return ResultSuccess; +} + +Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size) { program_id = metadata.GetTitleID(); ideal_core = metadata.GetMainThreadCore(); is_64bit_process = metadata.Is64BitProgram(); @@ -364,24 +356,24 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, return ResultLimitReached; } // Initialize proces address space - if (const ResultCode result{ - page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, 0x8000000, - code_size, KMemoryManager::Pool::Application)}; + if (const Result result{page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, + 0x8000000, code_size, + KMemoryManager::Pool::Application)}; result.IsError()) { return result; } // Map process code region - if (const ResultCode result{page_table->MapProcessCode(page_table->GetCodeRegionStart(), - code_size / PageSize, KMemoryState::Code, - KMemoryPermission::None)}; + if (const Result result{page_table->MapProcessCode(page_table->GetCodeRegionStart(), + code_size / PageSize, KMemoryState::Code, + KMemoryPermission::None)}; result.IsError()) { return result; } // Initialize process capabilities const auto& caps{metadata.GetKernelCapabilities()}; - if (const ResultCode result{ + if (const Result result{ capabilities.InitializeForUserProcess(caps.data(), caps.size(), *page_table)}; result.IsError()) { return result; @@ -401,11 +393,11 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, break; default: - UNREACHABLE(); + ASSERT(false); } // Create TLS region - tls_region_address = CreateTLSRegion(); + R_TRY(this->CreateThreadLocalRegion(std::addressof(tls_region_address))); memory_reservation.Commit(); return handle_table.Initialize(capabilities.GetHandleTableSize()); @@ -428,11 +420,11 @@ void KProcess::PrepareForTermination() { ChangeStatus(ProcessStatus::Exiting); const auto stop_threads = [this](const std::vector<KThread*>& in_thread_list) { - for (auto& thread : in_thread_list) { + for (auto* thread : in_thread_list) { if (thread->GetOwnerProcess() != this) continue; - if (thread == kernel.CurrentScheduler()->GetCurrentThread()) + if (thread == GetCurrentThreadPointer(kernel)) continue; // TODO(Subv): When are the other running/ready threads terminated? @@ -445,7 +437,7 @@ void KProcess::PrepareForTermination() { stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList()); - FreeTLSRegion(tls_region_address); + this->DeleteThreadLocalRegion(tls_region_address); tls_region_address = 0; if (resource_limit) { @@ -457,9 +449,6 @@ void KProcess::PrepareForTermination() { } void KProcess::Finalize() { - // Finalize the handle table and close any open handles. - handle_table.Finalize(); - // Free all shared memory infos. { auto it = shared_memory_list.begin(); @@ -484,67 +473,156 @@ void KProcess::Finalize() { resource_limit = nullptr; } + // Finalize the page table. + page_table.reset(); + // Perform inherited finalization. KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize(); } -/** - * Attempts to find a TLS page that contains a free slot for - * use by a thread. - * - * @returns If a page with an available slot is found, then an iterator - * pointing to the page is returned. Otherwise the end iterator - * is returned instead. - */ -static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) { - return std::find_if(tls_pages.begin(), tls_pages.end(), - [](const auto& page) { return page.HasAvailableSlots(); }); +Result KProcess::CreateThreadLocalRegion(VAddr* out) { + KThreadLocalPage* tlp = nullptr; + VAddr tlr = 0; + + // See if we can get a region from a partially used TLP. + { + KScopedSchedulerLock sl{kernel}; + + if (auto it = partially_used_tlp_tree.begin(); it != partially_used_tlp_tree.end()) { + tlr = it->Reserve(); + ASSERT(tlr != 0); + + if (it->IsAllUsed()) { + tlp = std::addressof(*it); + partially_used_tlp_tree.erase(it); + fully_used_tlp_tree.insert(*tlp); + } + + *out = tlr; + return ResultSuccess; + } + } + + // Allocate a new page. + tlp = KThreadLocalPage::Allocate(kernel); + R_UNLESS(tlp != nullptr, ResultOutOfMemory); + auto tlp_guard = SCOPE_GUARD({ KThreadLocalPage::Free(kernel, tlp); }); + + // Initialize the new page. + R_TRY(tlp->Initialize(kernel, this)); + + // Reserve a TLR. + tlr = tlp->Reserve(); + ASSERT(tlr != 0); + + // Insert into our tree. + { + KScopedSchedulerLock sl{kernel}; + if (tlp->IsAllUsed()) { + fully_used_tlp_tree.insert(*tlp); + } else { + partially_used_tlp_tree.insert(*tlp); + } + } + + // We succeeded! + tlp_guard.Cancel(); + *out = tlr; + return ResultSuccess; } -VAddr KProcess::CreateTLSRegion() { - KScopedSchedulerLock lock(kernel); - if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)}; - tls_page_iter != tls_pages.cend()) { - return *tls_page_iter->ReserveSlot(); +Result KProcess::DeleteThreadLocalRegion(VAddr addr) { + KThreadLocalPage* page_to_free = nullptr; + + // Release the region. + { + KScopedSchedulerLock sl{kernel}; + + // Try to find the page in the partially used list. + auto it = partially_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize)); + if (it == partially_used_tlp_tree.end()) { + // If we don't find it, it has to be in the fully used list. + it = fully_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize)); + R_UNLESS(it != fully_used_tlp_tree.end(), ResultInvalidAddress); + + // Release the region. + it->Release(addr); + + // Move the page out of the fully used list. + KThreadLocalPage* tlp = std::addressof(*it); + fully_used_tlp_tree.erase(it); + if (tlp->IsAllFree()) { + page_to_free = tlp; + } else { + partially_used_tlp_tree.insert(*tlp); + } + } else { + // Release the region. + it->Release(addr); + + // Handle the all-free case. + KThreadLocalPage* tlp = std::addressof(*it); + if (tlp->IsAllFree()) { + partially_used_tlp_tree.erase(it); + page_to_free = tlp; + } + } + } + + // If we should free the page it was in, do so. + if (page_to_free != nullptr) { + page_to_free->Finalize(); + + KThreadLocalPage::Free(kernel, page_to_free); } - Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()}; - ASSERT(tls_page_ptr); + return ResultSuccess; +} - const VAddr start{page_table->GetKernelMapRegionStart()}; - const VAddr size{page_table->GetKernelMapRegionEnd() - start}; - const PAddr tls_map_addr{kernel.System().DeviceMemory().GetPhysicalAddr(tls_page_ptr)}; - const VAddr tls_page_addr{page_table - ->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize, - KMemoryState::ThreadLocal, - KMemoryPermission::UserReadWrite, - tls_map_addr) - .ValueOr(0)}; +bool KProcess::InsertWatchpoint(Core::System& system, VAddr addr, u64 size, + DebugWatchpointType type) { + const auto watch{std::find_if(watchpoints.begin(), watchpoints.end(), [&](const auto& wp) { + return wp.type == DebugWatchpointType::None; + })}; - ASSERT(tls_page_addr); + if (watch == watchpoints.end()) { + return false; + } - std::memset(tls_page_ptr, 0, PageSize); - tls_pages.emplace_back(tls_page_addr); + watch->start_address = addr; + watch->end_address = addr + size; + watch->type = type; - const auto reserve_result{tls_pages.back().ReserveSlot()}; - ASSERT(reserve_result.has_value()); + for (VAddr page = Common::AlignDown(addr, PageSize); page < addr + size; page += PageSize) { + debug_page_refcounts[page]++; + system.Memory().MarkRegionDebug(page, PageSize, true); + } - return *reserve_result; + return true; } -void KProcess::FreeTLSRegion(VAddr tls_address) { - KScopedSchedulerLock lock(kernel); - const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE); - auto iter = - std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) { - return page.GetBaseAddress() == aligned_address; - }); +bool KProcess::RemoveWatchpoint(Core::System& system, VAddr addr, u64 size, + DebugWatchpointType type) { + const auto watch{std::find_if(watchpoints.begin(), watchpoints.end(), [&](const auto& wp) { + return wp.start_address == addr && wp.end_address == addr + size && wp.type == type; + })}; - // Something has gone very wrong if we're freeing a region - // with no actual page available. - ASSERT(iter != tls_pages.cend()); + if (watch == watchpoints.end()) { + return false; + } + + watch->start_address = 0; + watch->end_address = 0; + watch->type = DebugWatchpointType::None; + + for (VAddr page = Common::AlignDown(addr, PageSize); page < addr + size; page += PageSize) { + debug_page_refcounts[page]--; + if (!debug_page_refcounts[page]) { + system.Memory().MarkRegionDebug(page, PageSize, false); + } + } - iter->ReleaseSlot(tls_address); + return true; } void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { @@ -567,9 +645,10 @@ bool KProcess::IsSignaled() const { } KProcess::KProcess(KernelCore& kernel_) - : KAutoObjectWithSlabHeapAndContainer{kernel_}, - page_table{std::make_unique<KPageTable>(kernel_.System())}, handle_table{kernel_}, - address_arbiter{kernel_.System()}, condition_var{kernel_.System()}, state_lock{kernel_} {} + : KAutoObjectWithSlabHeapAndContainer{kernel_}, page_table{std::make_unique<KPageTable>( + kernel_.System())}, + handle_table{kernel_}, address_arbiter{kernel_.System()}, condition_var{kernel_.System()}, + state_lock{kernel_}, list_lock{kernel_} {} KProcess::~KProcess() = default; @@ -583,7 +662,7 @@ void KProcess::ChangeStatus(ProcessStatus new_status) { NotifyAvailable(); } -ResultCode KProcess::AllocateMainThreadStack(std::size_t stack_size) { +Result KProcess::AllocateMainThreadStack(std::size_t stack_size) { ASSERT(stack_size); // The kernel always ensures that the given stack size is page aligned. diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 38b446350..d56d73bab 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -1,20 +1,20 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2015 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <array> #include <cstddef> #include <list> +#include <map> #include <string> -#include <vector> #include "common/common_types.h" #include "core/hle/kernel/k_address_arbiter.h" #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_condition_variable.h" #include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_synchronization_object.h" +#include "core/hle/kernel/k_thread_local_page.h" #include "core/hle/kernel/k_worker_task.h" #include "core/hle/kernel/process_capability.h" #include "core/hle/kernel/slab_helpers.h" @@ -63,6 +63,25 @@ enum class ProcessStatus { DebugBreak, }; +enum class ProcessActivity : u32 { + Runnable, + Paused, +}; + +enum class DebugWatchpointType : u8 { + None = 0, + Read = 1 << 0, + Write = 1 << 1, + ReadOrWrite = Read | Write, +}; +DECLARE_ENUM_FLAG_OPERATORS(DebugWatchpointType); + +struct DebugWatchpoint { + VAddr start_address; + VAddr end_address; + DebugWatchpointType type; +}; + class KProcess final : public KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask> { KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject); @@ -90,8 +109,8 @@ public: static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4; - static ResultCode Initialize(KProcess* process, Core::System& system, std::string process_name, - ProcessType type); + static Result Initialize(KProcess* process, Core::System& system, std::string process_name, + ProcessType type, KResourceLimit* res_limit); /// Gets a reference to the process' page table. KPageTable& PageTable() { @@ -113,11 +132,11 @@ public: return handle_table; } - ResultCode SignalToAddress(VAddr address) { + Result SignalToAddress(VAddr address) { return condition_var.SignalToAddress(address); } - ResultCode WaitForAddress(Handle handle, VAddr address, u32 tag) { + Result WaitForAddress(Handle handle, VAddr address, u32 tag) { return condition_var.WaitForAddress(handle, address, tag); } @@ -125,17 +144,16 @@ public: return condition_var.Signal(cv_key, count); } - ResultCode WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) { + Result WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) { return condition_var.Wait(address, cv_key, tag, ns); } - ResultCode SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value, - s32 count) { + Result SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value, s32 count) { return address_arbiter.SignalToAddress(address, signal_type, value, count); } - ResultCode WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value, - s64 timeout) { + Result WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value, + s64 timeout) { return address_arbiter.WaitForAddress(address, arb_type, value, timeout); } @@ -282,17 +300,17 @@ public: u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const; /// Gets the list of all threads created with this process as their owner. - const std::list<const KThread*>& GetThreadList() const { + std::list<KThread*>& GetThreadList() { return thread_list; } /// Registers a thread as being created under this process, /// adding it to this process' thread list. - void RegisterThread(const KThread* thread); + void RegisterThread(KThread* thread); /// Unregisters a thread from this process, removing it /// from this process' thread list. - void UnregisterThread(const KThread* thread); + void UnregisterThread(KThread* thread); /// Clears the signaled state of the process if and only if it's signaled. /// @@ -302,7 +320,7 @@ public: /// @pre The process must be in a signaled state. If this is called on a /// process instance that is not signaled, ERR_INVALID_STATE will be /// returned. - ResultCode Reset(); + Result Reset(); /** * Loads process-specifics configuration info with metadata provided @@ -313,7 +331,7 @@ public: * @returns ResultSuccess if all relevant metadata was able to be * loaded and parsed. Otherwise, an error code is returned. */ - ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size); + Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size); /** * Starts the main application thread for this process. @@ -347,6 +365,8 @@ public: void DoWorkerTaskImpl(); + Result SetActivity(ProcessActivity activity); + void PinCurrentThread(s32 core_id); void UnpinCurrentThread(s32 core_id); void UnpinThread(KThread* thread); @@ -355,17 +375,30 @@ public: return state_lock; } - ResultCode AddSharedMemory(KSharedMemory* shmem, VAddr address, size_t size); + Result AddSharedMemory(KSharedMemory* shmem, VAddr address, size_t size); void RemoveSharedMemory(KSharedMemory* shmem, VAddr address, size_t size); /////////////////////////////////////////////////////////////////////////////////////////////// // Thread-local storage management // Marks the next available region as used and returns the address of the slot. - [[nodiscard]] VAddr CreateTLSRegion(); + [[nodiscard]] Result CreateThreadLocalRegion(VAddr* out); // Frees a used TLS slot identified by the given address - void FreeTLSRegion(VAddr tls_address); + Result DeleteThreadLocalRegion(VAddr addr); + + /////////////////////////////////////////////////////////////////////////////////////////////// + // Debug watchpoint management + + // Attempts to insert a watchpoint into a free slot. Returns false if none are available. + bool InsertWatchpoint(Core::System& system, VAddr addr, u64 size, DebugWatchpointType type); + + // Attempts to remove the watchpoint specified by the given parameters. + bool RemoveWatchpoint(Core::System& system, VAddr addr, u64 size, DebugWatchpointType type); + + const std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>& GetWatchpoints() const { + return watchpoints; + } private: void PinThread(s32 core_id, KThread* thread) { @@ -388,7 +421,7 @@ private: void ChangeStatus(ProcessStatus new_status); /// Allocates the main thread stack for the process, given the stack size in bytes. - ResultCode AllocateMainThreadStack(std::size_t stack_size); + Result AllocateMainThreadStack(std::size_t stack_size); /// Memory manager for this process std::unique_ptr<KPageTable> page_table; @@ -413,13 +446,6 @@ private: /// The ideal CPU core for this process, threads are scheduled on this core by default. u8 ideal_core = 0; - /// The Thread Local Storage area is allocated as processes create threads, - /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part - /// holds the TLS for a specific thread. This vector contains which parts are in use for each - /// page as a bitmask. - /// This vector will grow as more pages are allocated for new threads. - std::vector<TLSPage> tls_pages; - /// Contains the parsed process capability descriptors. ProcessCapabilities capabilities; @@ -429,7 +455,7 @@ private: bool is_64bit_process = true; /// Total running time for the process in ticks. - u64 total_process_running_time_ticks = 0; + std::atomic<u64> total_process_running_time_ticks = 0; /// Per-process handle table for storing created object handles in. KHandleTable handle_table; @@ -449,7 +475,7 @@ private: std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{}; /// List of threads that are running with this process as their owner. - std::list<const KThread*> thread_list; + std::list<KThread*> thread_list; /// List of shared memory that are running with this process as their owner. std::list<KSharedMemoryInfo*> shared_memory_list; @@ -478,10 +504,19 @@ private: std::array<KThread*, Core::Hardware::NUM_CPU_CORES> running_threads{}; std::array<u64, Core::Hardware::NUM_CPU_CORES> running_thread_idle_counts{}; std::array<KThread*, Core::Hardware::NUM_CPU_CORES> pinned_threads{}; + std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> watchpoints{}; + std::map<VAddr, u64> debug_page_refcounts; KThread* exception_thread{}; KLightLock state_lock; + KLightLock list_lock; + + using TLPTree = + Common::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>; + using TLPIterator = TLPTree::iterator; + TLPTree fully_used_tlp_tree; + TLPTree partially_used_tlp_tree; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_readable_event.cpp b/src/core/hle/kernel/k_readable_event.cpp index bf1db10d4..94c5464fe 100644 --- a/src/core/hle/kernel/k_readable_event.cpp +++ b/src/core/hle/kernel/k_readable_event.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "core/hle/kernel/k_event.h" @@ -28,7 +27,7 @@ void KReadableEvent::Destroy() { } } -ResultCode KReadableEvent::Signal() { +Result KReadableEvent::Signal() { KScopedSchedulerLock lk{kernel}; if (!is_signaled) { @@ -39,13 +38,13 @@ ResultCode KReadableEvent::Signal() { return ResultSuccess; } -ResultCode KReadableEvent::Clear() { +Result KReadableEvent::Clear() { Reset(); return ResultSuccess; } -ResultCode KReadableEvent::Reset() { +Result KReadableEvent::Reset() { KScopedSchedulerLock lk{kernel}; if (!is_signaled) { diff --git a/src/core/hle/kernel/k_readable_event.h b/src/core/hle/kernel/k_readable_event.h index 149fa78dd..18dcad289 100644 --- a/src/core/hle/kernel/k_readable_event.h +++ b/src/core/hle/kernel/k_readable_event.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -34,9 +33,9 @@ public: bool IsSignaled() const override; void Destroy() override; - ResultCode Signal(); - ResultCode Clear(); - ResultCode Reset(); + Result Signal(); + Result Clear(); + Result Reset(); private: bool is_signaled{}; diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp index 0c4bba66b..010dcf99e 100644 --- a/src/core/hle/kernel/k_resource_limit.cpp +++ b/src/core/hle/kernel/k_resource_limit.cpp @@ -1,8 +1,8 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/svc_results.h" @@ -73,7 +73,7 @@ s64 KResourceLimit::GetFreeValue(LimitableResource which) const { return value; } -ResultCode KResourceLimit::SetLimitValue(LimitableResource which, s64 value) { +Result KResourceLimit::SetLimitValue(LimitableResource which, s64 value) { const auto index = static_cast<std::size_t>(which); KScopedLightLock lk(lock); R_UNLESS(current_values[index] <= value, ResultInvalidState); @@ -151,4 +151,22 @@ void KResourceLimit::Release(LimitableResource which, s64 value, s64 hint) { } } +KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size) { + auto* resource_limit = KResourceLimit::Create(system.Kernel()); + resource_limit->Initialize(&system.CoreTiming()); + + // Initialize default resource limit values. + // TODO(bunnei): These values are the system defaults, the limits for service processes are + // lower. These should use the correct limit values. + + ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size) + .IsSuccess()); + ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); + ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess()); + ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess()); + ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); + + return resource_limit; +} + } // namespace Kernel diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h index fab6005ff..65c98c979 100644 --- a/src/core/hle/kernel/k_resource_limit.h +++ b/src/core/hle/kernel/k_resource_limit.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -9,7 +8,7 @@ #include "core/hle/kernel/k_light_condition_variable.h" #include "core/hle/kernel/k_light_lock.h" -union ResultCode; +union Result; namespace Core::Timing { class CoreTiming; @@ -47,7 +46,7 @@ public: s64 GetPeakValue(LimitableResource which) const; s64 GetFreeValue(LimitableResource which) const; - ResultCode SetLimitValue(LimitableResource which, s64 value); + Result SetLimitValue(LimitableResource which, s64 value); bool Reserve(LimitableResource which, s64 value); bool Reserve(LimitableResource which, s64 value, s64 timeout); @@ -67,4 +66,7 @@ private: KLightConditionVariable cond_var; const Core::Timing::CoreTiming* core_timing{}; }; + +KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size); + } // namespace Kernel diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index c96520828..c34ce7a17 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -1,9 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <bit> @@ -22,7 +18,6 @@ #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/physical_core.h" -#include "core/hle/kernel/time_manager.h" namespace Kernel { @@ -32,69 +27,185 @@ static void IncrementScheduledCount(Kernel::KThread* thread) { } } -void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule) { - auto scheduler = kernel.CurrentScheduler(); +KScheduler::KScheduler(KernelCore& kernel_) : kernel{kernel_} { + m_switch_fiber = std::make_shared<Common::Fiber>([this] { + while (true) { + ScheduleImplFiber(); + } + }); + + m_state.needs_scheduling = true; +} + +KScheduler::~KScheduler() = default; - u32 current_core{0xF}; - bool must_context_switch{}; - if (scheduler) { - current_core = scheduler->core_id; - // TODO(bunnei): Should be set to true when we deprecate single core - must_context_switch = !kernel.IsPhantomModeForSingleCore(); +void KScheduler::SetInterruptTaskRunnable() { + m_state.interrupt_task_runnable = true; + m_state.needs_scheduling = true; +} + +void KScheduler::RequestScheduleOnInterrupt() { + m_state.needs_scheduling = true; + + if (CanSchedule(kernel)) { + ScheduleOnInterrupt(); } +} - while (cores_pending_reschedule != 0) { - const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule)); - ASSERT(core < Core::Hardware::NUM_CPU_CORES); - if (!must_context_switch || core != current_core) { - auto& phys_core = kernel.PhysicalCore(core); - phys_core.Interrupt(); - } - cores_pending_reschedule &= ~(1ULL << core); +void KScheduler::DisableScheduling(KernelCore& kernel) { + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0); + GetCurrentThread(kernel).DisableDispatch(); +} + +void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) { + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 1); + + auto* scheduler{kernel.CurrentScheduler()}; + + if (!scheduler || kernel.IsPhantomModeForSingleCore()) { + KScheduler::RescheduleCores(kernel, cores_needing_scheduling); + KScheduler::RescheduleCurrentHLEThread(kernel); + return; } - for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) { - if (kernel.PhysicalCore(core_id).IsInterrupted()) { - KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_id)); - } + scheduler->RescheduleOtherCores(cores_needing_scheduling); + + if (GetCurrentThread(kernel).GetDisableDispatchCount() > 1) { + GetCurrentThread(kernel).EnableDispatch(); + } else { + scheduler->RescheduleCurrentCore(); } +} + +void KScheduler::RescheduleCurrentHLEThread(KernelCore& kernel) { + // HACK: we cannot schedule from this thread, it is not a core thread + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); + + // Special case to ensure dummy threads that are waiting block + GetCurrentThread(kernel).IfDummyThreadTryWait(); - if (must_context_switch) { - auto core_scheduler = kernel.CurrentScheduler(); - kernel.ExitSVCProfile(); - core_scheduler->RescheduleCurrentCore(); - kernel.EnterSVCProfile(); + ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting); + GetCurrentThread(kernel).EnableDispatch(); +} + +u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { + if (IsSchedulerUpdateNeeded(kernel)) { + return UpdateHighestPriorityThreadsImpl(kernel); + } else { + return 0; } } +void KScheduler::Schedule() { + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); + ASSERT(m_core_id == GetCurrentCoreId(kernel)); + + ScheduleImpl(); +} + +void KScheduler::ScheduleOnInterrupt() { + GetCurrentThread(kernel).DisableDispatch(); + Schedule(); + GetCurrentThread(kernel).EnableDispatch(); +} + +void KScheduler::PreemptSingleCore() { + GetCurrentThread(kernel).DisableDispatch(); + + auto* thread = GetCurrentThreadPointer(kernel); + auto& previous_scheduler = kernel.Scheduler(thread->GetCurrentCore()); + previous_scheduler.Unload(thread); + + Common::Fiber::YieldTo(thread->GetHostContext(), *m_switch_fiber); + + GetCurrentThread(kernel).EnableDispatch(); +} + +void KScheduler::RescheduleCurrentCore() { + ASSERT(!kernel.IsPhantomModeForSingleCore()); + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); + + GetCurrentThread(kernel).EnableDispatch(); + + if (m_state.needs_scheduling.load()) { + // Disable interrupts, and then check again if rescheduling is needed. + // KScopedInterruptDisable intr_disable; + + kernel.CurrentScheduler()->RescheduleCurrentCoreImpl(); + } +} + +void KScheduler::RescheduleCurrentCoreImpl() { + // Check that scheduling is needed. + if (m_state.needs_scheduling.load()) [[likely]] { + GetCurrentThread(kernel).DisableDispatch(); + Schedule(); + GetCurrentThread(kernel).EnableDispatch(); + } +} + +void KScheduler::Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id) { + // Set core ID/idle thread/interrupt task manager. + m_core_id = core_id; + m_idle_thread = idle_thread; + // m_state.idle_thread_stack = m_idle_thread->GetStackTop(); + // m_state.interrupt_task_manager = &kernel.GetInterruptTaskManager(); + + // Insert the main thread into the priority queue. + // { + // KScopedSchedulerLock lk{kernel}; + // GetPriorityQueue(kernel).PushBack(GetCurrentThreadPointer(kernel)); + // SetSchedulerUpdateNeeded(kernel); + // } + + // Bind interrupt handler. + // kernel.GetInterruptManager().BindHandler( + // GetSchedulerInterruptHandler(kernel), KInterruptName::Scheduler, m_core_id, + // KInterruptController::PriorityLevel::Scheduler, false, false); + + // Set the current thread. + m_current_thread = main_thread; +} + +void KScheduler::Activate() { + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); + + // m_state.should_count_idle = KTargetSystem::IsDebugMode(); + m_is_active = true; + RescheduleCurrentCore(); +} + +void KScheduler::OnThreadStart() { + GetCurrentThread(kernel).EnableDispatch(); +} + u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) { - KScopedSpinLock lk{guard}; - if (KThread* prev_highest_thread = state.highest_priority_thread; - prev_highest_thread != highest_thread) { - if (prev_highest_thread != nullptr) { + if (KThread* prev_highest_thread = m_state.highest_priority_thread; + prev_highest_thread != highest_thread) [[likely]] { + if (prev_highest_thread != nullptr) [[likely]] { IncrementScheduledCount(prev_highest_thread); - prev_highest_thread->SetLastScheduledTick(system.CoreTiming().GetCPUTicks()); + prev_highest_thread->SetLastScheduledTick(kernel.System().CoreTiming().GetCPUTicks()); } - if (state.should_count_idle) { - if (highest_thread != nullptr) { + if (m_state.should_count_idle) { + if (highest_thread != nullptr) [[likely]] { if (KProcess* process = highest_thread->GetOwnerProcess(); process != nullptr) { - process->SetRunningThread(core_id, highest_thread, state.idle_count); + process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count); } } else { - state.idle_count++; + m_state.idle_count++; } } - state.highest_priority_thread = highest_thread; - state.needs_scheduling.store(true); - return (1ULL << core_id); + m_state.highest_priority_thread = highest_thread; + m_state.needs_scheduling = true; + return (1ULL << m_core_id); } else { return 0; } } u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(IsSchedulerLockedByCurrentThread(kernel)); // Clear that we need to update. ClearSchedulerUpdateNeeded(kernel); @@ -103,18 +214,20 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { KThread* top_threads[Core::Hardware::NUM_CPU_CORES]; auto& priority_queue = GetPriorityQueue(kernel); - /// We want to go over all cores, finding the highest priority thread and determining if - /// scheduling is needed for that core. + // We want to go over all cores, finding the highest priority thread and determining if + // scheduling is needed for that core. for (size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { KThread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id)); if (top_thread != nullptr) { - // If the thread has no waiters, we need to check if the process has a thread pinned. - if (top_thread->GetNumKernelWaiters() == 0) { - if (KProcess* parent = top_thread->GetOwnerProcess(); parent != nullptr) { - if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id)); - pinned != nullptr && pinned != top_thread) { - // We prefer our parent's pinned thread if possible. However, we also don't - // want to schedule un-runnable threads. + // We need to check if the thread's process has a pinned thread. + if (KProcess* parent = top_thread->GetOwnerProcess()) { + // Check that there's a pinned thread other than the current top thread. + if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id)); + pinned != nullptr && pinned != top_thread) { + // We need to prefer threads with kernel waiters to the pinned thread. + if (top_thread->GetNumKernelWaiters() == + 0 /* && top_thread != parent->GetExceptionThread() */) { + // If the pinned thread is runnable, use it. if (pinned->GetRawState() == ThreadState::Runnable) { top_thread = pinned; } else { @@ -134,7 +247,8 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { // Idle cores are bad. We're going to try to migrate threads to each idle core in turn. while (idle_cores != 0) { - const auto core_id = static_cast<u32>(std::countr_zero(idle_cores)); + const s32 core_id = static_cast<s32>(std::countr_zero(idle_cores)); + if (KThread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) { s32 migration_candidates[Core::Hardware::NUM_CPU_CORES]; size_t num_candidates = 0; @@ -155,7 +269,6 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { // The suggested thread isn't bound to its core, so we can migrate it! suggested->SetActiveCore(core_id); priority_queue.ChangeCore(suggested_core, suggested); - top_threads[core_id] = suggested; cores_needing_scheduling |= kernel.Scheduler(core_id).UpdateHighestPriorityThread(top_threads[core_id]); @@ -188,7 +301,6 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { // Perform the migration. suggested->SetActiveCore(core_id); priority_queue.ChangeCore(candidate_core, suggested); - top_threads[core_id] = suggested; cores_needing_scheduling |= kernel.Scheduler(core_id).UpdateHighestPriorityThread( @@ -205,24 +317,210 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { return cores_needing_scheduling; } +void KScheduler::SwitchThread(KThread* next_thread) { + KProcess* const cur_process = kernel.CurrentProcess(); + KThread* const cur_thread = GetCurrentThreadPointer(kernel); + + // We never want to schedule a null thread, so use the idle thread if we don't have a next. + if (next_thread == nullptr) { + next_thread = m_idle_thread; + } + + if (next_thread->GetCurrentCore() != m_core_id) { + next_thread->SetCurrentCore(m_core_id); + } + + // If we're not actually switching thread, there's nothing to do. + if (next_thread == cur_thread) { + return; + } + + // Next thread is now known not to be nullptr, and must not be dispatchable. + ASSERT(next_thread->GetDisableDispatchCount() == 1); + ASSERT(!next_thread->IsDummyThread()); + + // Update the CPU time tracking variables. + const s64 prev_tick = m_last_context_switch_time; + const s64 cur_tick = kernel.System().CoreTiming().GetCPUTicks(); + const s64 tick_diff = cur_tick - prev_tick; + cur_thread->AddCpuTime(m_core_id, tick_diff); + if (cur_process != nullptr) { + cur_process->UpdateCPUTimeTicks(tick_diff); + } + m_last_context_switch_time = cur_tick; + + // Update our previous thread. + if (cur_process != nullptr) { + if (!cur_thread->IsTerminationRequested() && cur_thread->GetActiveCore() == m_core_id) + [[likely]] { + m_state.prev_thread = cur_thread; + } else { + m_state.prev_thread = nullptr; + } + } + + // Switch the current process, if we're switching processes. + // if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) { + // KProcess::Switch(cur_process, next_process); + // } + + // Set the new thread. + SetCurrentThread(kernel, next_thread); + m_current_thread = next_thread; + + // Set the new Thread Local region. + // cpu::SwitchThreadLocalRegion(GetInteger(next_thread->GetThreadLocalRegionAddress())); +} + +void KScheduler::ScheduleImpl() { + // First, clear the needs scheduling bool. + m_state.needs_scheduling.store(false, std::memory_order_seq_cst); + + // Load the appropriate thread pointers for scheduling. + KThread* const cur_thread{GetCurrentThreadPointer(kernel)}; + KThread* highest_priority_thread{m_state.highest_priority_thread}; + + // Check whether there are runnable interrupt tasks. + if (m_state.interrupt_task_runnable) { + // The interrupt task is runnable. + // We want to switch to the interrupt task/idle thread. + highest_priority_thread = nullptr; + } + + // If there aren't, we want to check if the highest priority thread is the same as the current + // thread. + if (highest_priority_thread == cur_thread) { + // If they're the same, then we can just return. + return; + } + + // The highest priority thread is not the same as the current thread. + // Jump to the switcher and continue executing from there. + m_switch_cur_thread = cur_thread; + m_switch_highest_priority_thread = highest_priority_thread; + m_switch_from_schedule = true; + Common::Fiber::YieldTo(cur_thread->host_context, *m_switch_fiber); + + // Returning from ScheduleImpl occurs after this thread has been scheduled again. +} + +void KScheduler::ScheduleImplFiber() { + KThread* const cur_thread{m_switch_cur_thread}; + KThread* highest_priority_thread{m_switch_highest_priority_thread}; + + // If we're not coming from scheduling (i.e., we came from SC preemption), + // we should restart the scheduling loop directly. Not accurate to HOS. + if (!m_switch_from_schedule) { + goto retry; + } + + // Mark that we are not coming from scheduling anymore. + m_switch_from_schedule = false; + + // Save the original thread context. + Unload(cur_thread); + + // The current thread's context has been entirely taken care of. + // Now we want to loop until we successfully switch the thread context. + while (true) { + // We're starting to try to do the context switch. + // Check if the highest priority thread is null. + if (!highest_priority_thread) { + // The next thread is nullptr! + + // Switch to the idle thread. Note: HOS treats idling as a special case for + // performance. This is not *required* for yuzu's purposes, and for singlecore + // compatibility, we can just move the logic that would go here into the execution + // of the idle thread. If we ever remove singlecore, we should implement this + // accurately to HOS. + highest_priority_thread = m_idle_thread; + } + + // We want to try to lock the highest priority thread's context. + // Try to take it. + while (!highest_priority_thread->context_guard.try_lock()) { + // The highest priority thread's context is already locked. + // Check if we need scheduling. If we don't, we can retry directly. + if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) { + // If we do, another core is interfering, and we must start again. + goto retry; + } + } + + // It's time to switch the thread. + // Switch to the highest priority thread. + SwitchThread(highest_priority_thread); + + // Check if we need scheduling. If we do, then we can't complete the switch and should + // retry. + if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) { + // Our switch failed. + // We should unlock the thread context, and then retry. + highest_priority_thread->context_guard.unlock(); + goto retry; + } else { + break; + } + + retry: + + // We failed to successfully do the context switch, and need to retry. + // Clear needs_scheduling. + m_state.needs_scheduling.store(false, std::memory_order_seq_cst); + + // Refresh the highest priority thread. + highest_priority_thread = m_state.highest_priority_thread; + } + + // Reload the guest thread context. + Reload(highest_priority_thread); + + // Reload the host thread. + Common::Fiber::YieldTo(m_switch_fiber, *highest_priority_thread->host_context); +} + +void KScheduler::Unload(KThread* thread) { + auto& cpu_core = kernel.System().ArmInterface(m_core_id); + cpu_core.SaveContext(thread->GetContext32()); + cpu_core.SaveContext(thread->GetContext64()); + // Save the TPIDR_EL0 system register in case it was modified. + thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); + cpu_core.ClearExclusiveState(); + + // Check if the thread is terminated by checking the DPC flags. + if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) { + // The thread isn't terminated, so we want to unlock it. + thread->context_guard.unlock(); + } +} + +void KScheduler::Reload(KThread* thread) { + auto& cpu_core = kernel.System().ArmInterface(m_core_id); + cpu_core.LoadContext(thread->GetContext32()); + cpu_core.LoadContext(thread->GetContext64()); + cpu_core.SetTlsAddress(thread->GetTLSAddress()); + cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0()); + cpu_core.LoadWatchpointArray(thread->GetOwnerProcess()->GetWatchpoints()); + cpu_core.ClearExclusiveState(); +} + void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(IsSchedulerLockedByCurrentThread(kernel)); for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; ++i) { // Get an atomic reference to the core scheduler's previous thread. - std::atomic_ref<KThread*> prev_thread(kernel.Scheduler(static_cast<s32>(i)).prev_thread); - static_assert(std::atomic_ref<KThread*>::is_always_lock_free); + auto& prev_thread{kernel.Scheduler(i).m_state.prev_thread}; // Atomically clear the previous thread if it's our target. KThread* compare = thread; - prev_thread.compare_exchange_strong(compare, nullptr); + prev_thread.compare_exchange_strong(compare, nullptr, std::memory_order_seq_cst); } } void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state) { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(IsSchedulerLockedByCurrentThread(kernel)); // Check if the state has changed, because if it hasn't there's nothing to do. - const auto cur_state = thread->GetRawState(); + const ThreadState cur_state = thread->GetRawState(); if (cur_state == old_state) { return; } @@ -242,12 +540,12 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, Threa } void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority) { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(IsSchedulerLockedByCurrentThread(kernel)); // If the thread is runnable, we want to change its priority in the queue. if (thread->GetRawState() == ThreadState::Runnable) { GetPriorityQueue(kernel).ChangePriority(old_priority, - thread == kernel.GetCurrentEmuThread(), thread); + thread == GetCurrentThreadPointer(kernel), thread); IncrementScheduledCount(thread); SetSchedulerUpdateNeeded(kernel); } @@ -255,7 +553,7 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s3 void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread, const KAffinityMask& old_affinity, s32 old_core) { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(IsSchedulerLockedByCurrentThread(kernel)); // If the thread is runnable, we want to change its affinity in the queue. if (thread->GetRawState() == ThreadState::Runnable) { @@ -265,15 +563,14 @@ void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread } } -void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) { - ASSERT(system.GlobalSchedulerContext().IsLocked()); +void KScheduler::RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 priority) { + ASSERT(IsSchedulerLockedByCurrentThread(kernel)); // Get a reference to the priority queue. - auto& kernel = system.Kernel(); auto& priority_queue = GetPriorityQueue(kernel); // Rotate the front of the queue to the end. - KThread* top_thread = priority_queue.GetScheduledFront(cpu_core_id, priority); + KThread* top_thread = priority_queue.GetScheduledFront(core_id, priority); KThread* next_thread = nullptr; if (top_thread != nullptr) { next_thread = priority_queue.MoveToScheduledBack(top_thread); @@ -285,7 +582,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) { // While we have a suggested thread, try to migrate it! { - KThread* suggested = priority_queue.GetSuggestedFront(cpu_core_id, priority); + KThread* suggested = priority_queue.GetSuggestedFront(core_id, priority); while (suggested != nullptr) { // Check if the suggested thread is the top thread on its core. const s32 suggested_core = suggested->GetActiveCore(); @@ -306,7 +603,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) { // to the front of the queue. if (top_on_suggested_core == nullptr || top_on_suggested_core->GetPriority() >= HighestCoreMigrationAllowedPriority) { - suggested->SetActiveCore(cpu_core_id); + suggested->SetActiveCore(core_id); priority_queue.ChangeCore(suggested_core, suggested, true); IncrementScheduledCount(suggested); break; @@ -314,22 +611,21 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) { } // Get the next suggestion. - suggested = priority_queue.GetSamePriorityNext(cpu_core_id, suggested); + suggested = priority_queue.GetSamePriorityNext(core_id, suggested); } } // Now that we might have migrated a thread with the same priority, check if we can do better. - { - KThread* best_thread = priority_queue.GetScheduledFront(cpu_core_id); - if (best_thread == GetCurrentThread()) { - best_thread = priority_queue.GetScheduledNext(cpu_core_id, best_thread); + KThread* best_thread = priority_queue.GetScheduledFront(core_id); + if (best_thread == GetCurrentThreadPointer(kernel)) { + best_thread = priority_queue.GetScheduledNext(core_id, best_thread); } // If the best thread we can choose has a priority the same or worse than ours, try to // migrate a higher priority thread. if (best_thread != nullptr && best_thread->GetPriority() >= priority) { - KThread* suggested = priority_queue.GetSuggestedFront(cpu_core_id); + KThread* suggested = priority_queue.GetSuggestedFront(core_id); while (suggested != nullptr) { // If the suggestion's priority is the same as ours, don't bother. if (suggested->GetPriority() >= best_thread->GetPriority()) { @@ -348,7 +644,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) { if (top_on_suggested_core == nullptr || top_on_suggested_core->GetPriority() >= HighestCoreMigrationAllowedPriority) { - suggested->SetActiveCore(cpu_core_id); + suggested->SetActiveCore(core_id); priority_queue.ChangeCore(suggested_core, suggested, true); IncrementScheduledCount(suggested); break; @@ -356,7 +652,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) { } // Get the next suggestion. - suggested = priority_queue.GetSuggestedNext(cpu_core_id, suggested); + suggested = priority_queue.GetSuggestedNext(core_id, suggested); } } } @@ -365,71 +661,13 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) { SetSchedulerUpdateNeeded(kernel); } -bool KScheduler::CanSchedule(KernelCore& kernel) { - return kernel.GetCurrentEmuThread()->GetDisableDispatchCount() <= 1; -} - -bool KScheduler::IsSchedulerUpdateNeeded(const KernelCore& kernel) { - return kernel.GlobalSchedulerContext().scheduler_update_needed.load(std::memory_order_acquire); -} - -void KScheduler::SetSchedulerUpdateNeeded(KernelCore& kernel) { - kernel.GlobalSchedulerContext().scheduler_update_needed.store(true, std::memory_order_release); -} - -void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) { - kernel.GlobalSchedulerContext().scheduler_update_needed.store(false, std::memory_order_release); -} - -void KScheduler::DisableScheduling(KernelCore& kernel) { - // If we are shutting down the kernel, none of this is relevant anymore. - if (kernel.IsShuttingDown()) { - return; - } - - ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0); - GetCurrentThreadPointer(kernel)->DisableDispatch(); -} - -void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) { - // If we are shutting down the kernel, none of this is relevant anymore. - if (kernel.IsShuttingDown()) { - return; - } - - auto* current_thread = GetCurrentThreadPointer(kernel); - - ASSERT(current_thread->GetDisableDispatchCount() >= 1); - - if (current_thread->GetDisableDispatchCount() > 1) { - current_thread->EnableDispatch(); - } else { - RescheduleCores(kernel, cores_needing_scheduling); - } - - // Special case to ensure dummy threads that are waiting block. - current_thread->IfDummyThreadTryWait(); -} - -u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { - if (IsSchedulerUpdateNeeded(kernel)) { - return UpdateHighestPriorityThreadsImpl(kernel); - } else { - return 0; - } -} - -KSchedulerPriorityQueue& KScheduler::GetPriorityQueue(KernelCore& kernel) { - return kernel.GlobalSchedulerContext().priority_queue; -} - void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) { // Validate preconditions. ASSERT(CanSchedule(kernel)); ASSERT(kernel.CurrentProcess() != nullptr); // Get the current thread and process. - KThread& cur_thread = Kernel::GetCurrentThread(kernel); + KThread& cur_thread = GetCurrentThread(kernel); KProcess& cur_process = *kernel.CurrentProcess(); // If the thread's yield count matches, there's nothing for us to do. @@ -442,7 +680,7 @@ void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) { // Perform the yield. { - KScopedSchedulerLock lock(kernel); + KScopedSchedulerLock sl{kernel}; const auto cur_state = cur_thread.GetRawState(); if (cur_state == ThreadState::Runnable) { @@ -468,7 +706,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) { ASSERT(kernel.CurrentProcess() != nullptr); // Get the current thread and process. - KThread& cur_thread = Kernel::GetCurrentThread(kernel); + KThread& cur_thread = GetCurrentThread(kernel); KProcess& cur_process = *kernel.CurrentProcess(); // If the thread's yield count matches, there's nothing for us to do. @@ -481,7 +719,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) { // Perform the yield. { - KScopedSchedulerLock lock(kernel); + KScopedSchedulerLock sl{kernel}; const auto cur_state = cur_thread.GetRawState(); if (cur_state == ThreadState::Runnable) { @@ -501,7 +739,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) { if (KThread* running_on_suggested_core = (suggested_core >= 0) - ? kernel.Scheduler(suggested_core).state.highest_priority_thread + ? kernel.Scheduler(suggested_core).m_state.highest_priority_thread : nullptr; running_on_suggested_core != suggested) { // If the current thread's priority is higher than our suggestion's we prefer @@ -556,7 +794,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) { ASSERT(kernel.CurrentProcess() != nullptr); // Get the current thread and process. - KThread& cur_thread = Kernel::GetCurrentThread(kernel); + KThread& cur_thread = GetCurrentThread(kernel); KProcess& cur_process = *kernel.CurrentProcess(); // If the thread's yield count matches, there's nothing for us to do. @@ -569,7 +807,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) { // Perform the yield. { - KScopedSchedulerLock lock(kernel); + KScopedSchedulerLock sl{kernel}; const auto cur_state = cur_thread.GetRawState(); if (cur_state == ThreadState::Runnable) { @@ -626,219 +864,19 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) { } } -KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, core_id{core_id_} { - switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this); - state.needs_scheduling.store(true); - state.interrupt_task_thread_runnable = false; - state.should_count_idle = false; - state.idle_count = 0; - state.idle_thread_stack = nullptr; - state.highest_priority_thread = nullptr; -} - -void KScheduler::Finalize() { - if (idle_thread) { - idle_thread->Close(); - idle_thread = nullptr; - } -} - -KScheduler::~KScheduler() { - ASSERT(!idle_thread); -} - -KThread* KScheduler::GetCurrentThread() const { - if (auto result = current_thread.load(); result) { - return result; - } - return idle_thread; -} - -u64 KScheduler::GetLastContextSwitchTicks() const { - return last_context_switch_time; -} - -void KScheduler::RescheduleCurrentCore() { - ASSERT(GetCurrentThread()->GetDisableDispatchCount() == 1); - - auto& phys_core = system.Kernel().PhysicalCore(core_id); - if (phys_core.IsInterrupted()) { - phys_core.ClearInterrupt(); +void KScheduler::RescheduleOtherCores(u64 cores_needing_scheduling) { + if (const u64 core_mask = cores_needing_scheduling & ~(1ULL << m_core_id); core_mask != 0) { + RescheduleCores(kernel, core_mask); } - - guard.Lock(); - if (state.needs_scheduling.load()) { - Schedule(); - } else { - GetCurrentThread()->EnableDispatch(); - guard.Unlock(); - } -} - -void KScheduler::OnThreadStart() { - SwitchContextStep2(); } -void KScheduler::Unload(KThread* thread) { - ASSERT(thread); - - LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr"); - - if (thread->IsCallingSvc()) { - thread->ClearIsCallingSvc(); - } - - auto& physical_core = system.Kernel().PhysicalCore(core_id); - if (!physical_core.IsInitialized()) { - return; - } - - Core::ARM_Interface& cpu_core = physical_core.ArmInterface(); - cpu_core.SaveContext(thread->GetContext32()); - cpu_core.SaveContext(thread->GetContext64()); - // Save the TPIDR_EL0 system register in case it was modified. - thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); - cpu_core.ClearExclusiveState(); - - if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) { - prev_thread = thread; - } else { - prev_thread = nullptr; - } - - thread->context_guard.Unlock(); -} - -void KScheduler::Reload(KThread* thread) { - LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread->GetName()); - - Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); - cpu_core.LoadContext(thread->GetContext32()); - cpu_core.LoadContext(thread->GetContext64()); - cpu_core.SetTlsAddress(thread->GetTLSAddress()); - cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0()); - cpu_core.ClearExclusiveState(); -} - -void KScheduler::SwitchContextStep2() { - // Load context of new thread - Reload(GetCurrentThread()); - - RescheduleCurrentCore(); -} - -void KScheduler::ScheduleImpl() { - KThread* previous_thread = GetCurrentThread(); - KThread* next_thread = state.highest_priority_thread; - - state.needs_scheduling.store(false); - - // We never want to schedule a null thread, so use the idle thread if we don't have a next. - if (next_thread == nullptr) { - next_thread = idle_thread; - } - - if (next_thread->GetCurrentCore() != core_id) { - next_thread->SetCurrentCore(core_id); - } - - // We never want to schedule a dummy thread, as these are only used by host threads for locking. - if (next_thread->GetThreadType() == ThreadType::Dummy) { - ASSERT_MSG(false, "Dummy threads should never be scheduled!"); - next_thread = idle_thread; - } - - // If we're not actually switching thread, there's nothing to do. - if (next_thread == current_thread.load()) { - previous_thread->EnableDispatch(); - guard.Unlock(); - return; - } - - // Update the CPU time tracking variables. - KProcess* const previous_process = system.Kernel().CurrentProcess(); - UpdateLastContextSwitchTime(previous_thread, previous_process); - - // Save context for previous thread - Unload(previous_thread); - - std::shared_ptr<Common::Fiber>* old_context; - old_context = &previous_thread->GetHostContext(); - - // Set the new thread. - current_thread.store(next_thread); - - guard.Unlock(); - - Common::Fiber::YieldTo(*old_context, *switch_fiber); - /// When a thread wakes up, the scheduler may have changed to other in another core. - auto& next_scheduler = *system.Kernel().CurrentScheduler(); - next_scheduler.SwitchContextStep2(); -} - -void KScheduler::OnSwitch(void* this_scheduler) { - KScheduler* sched = static_cast<KScheduler*>(this_scheduler); - sched->SwitchToCurrent(); -} - -void KScheduler::SwitchToCurrent() { - while (true) { - { - KScopedSpinLock lk{guard}; - current_thread.store(state.highest_priority_thread); - state.needs_scheduling.store(false); +void KScheduler::RescheduleCores(KernelCore& kernel, u64 core_mask) { + // Send IPI + for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { + if (core_mask & (1ULL << i)) { + kernel.PhysicalCore(i).Interrupt(); } - const auto is_switch_pending = [this] { - KScopedSpinLock lk{guard}; - return state.needs_scheduling.load(); - }; - do { - auto next_thread = current_thread.load(); - if (next_thread != nullptr) { - const auto locked = next_thread->context_guard.TryLock(); - if (state.needs_scheduling.load()) { - next_thread->context_guard.Unlock(); - break; - } - if (next_thread->GetActiveCore() != core_id) { - next_thread->context_guard.Unlock(); - break; - } - if (!locked) { - continue; - } - } - auto thread = next_thread ? next_thread : idle_thread; - Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext()); - } while (!is_switch_pending()); } } -void KScheduler::UpdateLastContextSwitchTime(KThread* thread, KProcess* process) { - const u64 prev_switch_ticks = last_context_switch_time; - const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks(); - const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; - - if (thread != nullptr) { - thread->AddCpuTime(core_id, update_ticks); - } - - if (process != nullptr) { - process->UpdateCPUTimeTicks(update_ticks); - } - - last_context_switch_time = most_recent_switch_ticks; -} - -void KScheduler::Initialize() { - idle_thread = KThread::Create(system.Kernel()); - ASSERT(KThread::InitializeIdleThread(system, idle_thread, core_id).IsSuccess()); - idle_thread->SetName(fmt::format("IdleThread:{}", core_id)); -} - -KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel) - : KScopedLock(kernel.GlobalSchedulerContext().SchedulerLock()) {} - -KScopedSchedulerLock::~KScopedSchedulerLock() = default; - } // namespace Kernel diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index 82fcd99e7..534321d8d 100644 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -12,6 +11,7 @@ #include "core/hle/kernel/k_scheduler_lock.h" #include "core/hle/kernel/k_scoped_lock.h" #include "core/hle/kernel/k_spin_lock.h" +#include "core/hle/kernel/k_thread.h" namespace Common { class Fiber; @@ -24,188 +24,150 @@ class System; namespace Kernel { class KernelCore; +class KInterruptTaskManager; class KProcess; -class SchedulerLock; class KThread; +class KScopedDisableDispatch; +class KScopedSchedulerLock; +class KScopedSchedulerLockAndSleep; class KScheduler final { public: - explicit KScheduler(Core::System& system_, s32 core_id_); - ~KScheduler(); - - void Finalize(); + YUZU_NON_COPYABLE(KScheduler); + YUZU_NON_MOVEABLE(KScheduler); - /// Reschedules to the next available thread (call after current thread is suspended) - void RescheduleCurrentCore(); + using LockType = KAbstractSchedulerLock<KScheduler>; - /// Reschedules cores pending reschedule, to be called on EnableScheduling. - static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule); + explicit KScheduler(KernelCore& kernel); + ~KScheduler(); - /// The next two are for SingleCore Only. - /// Unload current thread before preempting core. + void Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id); + void Activate(); + void OnThreadStart(); void Unload(KThread* thread); - - /// Reload current thread after core preemption. void Reload(KThread* thread); - /// Gets the current running thread - [[nodiscard]] KThread* GetCurrentThread() const; + void SetInterruptTaskRunnable(); + void RequestScheduleOnInterrupt(); + void PreemptSingleCore(); - /// Gets the idle thread - [[nodiscard]] KThread* GetIdleThread() const { - return idle_thread; + u64 GetIdleCount() { + return m_state.idle_count; } - /// Returns true if the scheduler is idle - [[nodiscard]] bool IsIdle() const { - return GetCurrentThread() == idle_thread; + KThread* GetIdleThread() const { + return m_idle_thread; } - /// Gets the timestamp for the last context switch in ticks. - [[nodiscard]] u64 GetLastContextSwitchTicks() const; - - [[nodiscard]] bool ContextSwitchPending() const { - return state.needs_scheduling.load(std::memory_order_relaxed); + bool IsIdle() const { + return m_current_thread.load() == m_idle_thread; } - void Initialize(); - - void OnThreadStart(); + KThread* GetPreviousThread() const { + return m_state.prev_thread; + } - [[nodiscard]] std::shared_ptr<Common::Fiber>& ControlContext() { - return switch_fiber; + KThread* GetSchedulerCurrentThread() const { + return m_current_thread.load(); } - [[nodiscard]] const std::shared_ptr<Common::Fiber>& ControlContext() const { - return switch_fiber; + s64 GetLastContextSwitchTime() const { + return m_last_context_switch_time; } - [[nodiscard]] u64 UpdateHighestPriorityThread(KThread* highest_thread); + // Static public API. + static bool CanSchedule(KernelCore& kernel) { + return GetCurrentThread(kernel).GetDisableDispatchCount() == 0; + } + static bool IsSchedulerLockedByCurrentThread(KernelCore& kernel) { + return kernel.GlobalSchedulerContext().scheduler_lock.IsLockedByCurrentThread(); + } - /** - * Takes a thread and moves it to the back of the it's priority list. - * - * @note This operation can be redundant and no scheduling is changed if marked as so. - */ - static void YieldWithoutCoreMigration(KernelCore& kernel); + static bool IsSchedulerUpdateNeeded(KernelCore& kernel) { + return kernel.GlobalSchedulerContext().scheduler_update_needed; + } + static void SetSchedulerUpdateNeeded(KernelCore& kernel) { + kernel.GlobalSchedulerContext().scheduler_update_needed = true; + } + static void ClearSchedulerUpdateNeeded(KernelCore& kernel) { + kernel.GlobalSchedulerContext().scheduler_update_needed = false; + } - /** - * Takes a thread and moves it to the back of the it's priority list. - * Afterwards, tries to pick a suggested thread from the suggested queue that has worse time or - * a better priority than the next thread in the core. - * - * @note This operation can be redundant and no scheduling is changed if marked as so. - */ - static void YieldWithCoreMigration(KernelCore& kernel); + static void DisableScheduling(KernelCore& kernel); + static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling); - /** - * Takes a thread and moves it out of the scheduling queue. - * and into the suggested queue. If no thread can be scheduled afterwards in that core, - * a suggested thread is obtained instead. - * - * @note This operation can be redundant and no scheduling is changed if marked as so. - */ - static void YieldToAnyThread(KernelCore& kernel); + static u64 UpdateHighestPriorityThreads(KernelCore& kernel); static void ClearPreviousThread(KernelCore& kernel, KThread* thread); - /// Notify the scheduler a thread's status has changed. static void OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state); - - /// Notify the scheduler a thread's priority has changed. static void OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority); - - /// Notify the scheduler a thread's core and/or affinity mask has changed. static void OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread, const KAffinityMask& old_affinity, s32 old_core); - static bool CanSchedule(KernelCore& kernel); - static bool IsSchedulerUpdateNeeded(const KernelCore& kernel); - static void SetSchedulerUpdateNeeded(KernelCore& kernel); - static void ClearSchedulerUpdateNeeded(KernelCore& kernel); - static void DisableScheduling(KernelCore& kernel); - static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling); - [[nodiscard]] static u64 UpdateHighestPriorityThreads(KernelCore& kernel); + static void RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 priority); + static void RescheduleCores(KernelCore& kernel, u64 cores_needing_scheduling); + + static void YieldWithoutCoreMigration(KernelCore& kernel); + static void YieldWithCoreMigration(KernelCore& kernel); + static void YieldToAnyThread(KernelCore& kernel); private: - friend class GlobalSchedulerContext; - - /** - * Takes care of selecting the new scheduled threads in three steps: - * - * 1. First a thread is selected from the top of the priority queue. If no thread - * is obtained then we move to step two, else we are done. - * - * 2. Second we try to get a suggested thread that's not assigned to any core or - * that is not the top thread in that core. - * - * 3. Third is no suggested thread is found, we do a second pass and pick a running - * thread in another core and swap it with its current thread. - * - * returns the cores needing scheduling. - */ - [[nodiscard]] static u64 UpdateHighestPriorityThreadsImpl(KernelCore& kernel); - - [[nodiscard]] static KSchedulerPriorityQueue& GetPriorityQueue(KernelCore& kernel); - - void RotateScheduledQueue(s32 cpu_core_id, s32 priority); - - void Schedule() { - ASSERT(GetCurrentThread()->GetDisableDispatchCount() == 1); - this->ScheduleImpl(); + // Static private API. + static KSchedulerPriorityQueue& GetPriorityQueue(KernelCore& kernel) { + return kernel.GlobalSchedulerContext().priority_queue; } + static u64 UpdateHighestPriorityThreadsImpl(KernelCore& kernel); - /// Switches the CPU's active thread context to that of the specified thread - void ScheduleImpl(); - - /// When a thread wakes up, it must run this through it's new scheduler - void SwitchContextStep2(); + static void RescheduleCurrentHLEThread(KernelCore& kernel); - /** - * Called on every context switch to update the internal timestamp - * This also updates the running time ticks for the given thread and - * process using the following difference: - * - * ticks += most_recent_ticks - last_context_switch_ticks - * - * The internal tick timestamp for the scheduler is simply the - * most recent tick count retrieved. No special arithmetic is - * applied to it. - */ - void UpdateLastContextSwitchTime(KThread* thread, KProcess* process); + // Instanced private API. + void ScheduleImpl(); + void ScheduleImplFiber(); + void SwitchThread(KThread* next_thread); - static void OnSwitch(void* this_scheduler); - void SwitchToCurrent(); + void Schedule(); + void ScheduleOnInterrupt(); - KThread* prev_thread{}; - std::atomic<KThread*> current_thread{}; + void RescheduleOtherCores(u64 cores_needing_scheduling); + void RescheduleCurrentCore(); + void RescheduleCurrentCoreImpl(); - KThread* idle_thread{}; + u64 UpdateHighestPriorityThread(KThread* thread); - std::shared_ptr<Common::Fiber> switch_fiber{}; +private: + friend class KScopedDisableDispatch; struct SchedulingState { - std::atomic<bool> needs_scheduling{}; - bool interrupt_task_thread_runnable{}; - bool should_count_idle{}; - u64 idle_count{}; - KThread* highest_priority_thread{}; - void* idle_thread_stack{}; + std::atomic<bool> needs_scheduling{false}; + bool interrupt_task_runnable{false}; + bool should_count_idle{false}; + u64 idle_count{0}; + KThread* highest_priority_thread{nullptr}; + void* idle_thread_stack{nullptr}; + std::atomic<KThread*> prev_thread{nullptr}; + KInterruptTaskManager* interrupt_task_manager{nullptr}; }; - SchedulingState state; - - Core::System& system; - u64 last_context_switch_time{}; - const s32 core_id; - - KSpinLock guard{}; + KernelCore& kernel; + SchedulingState m_state; + bool m_is_active{false}; + s32 m_core_id{0}; + s64 m_last_context_switch_time{0}; + KThread* m_idle_thread{nullptr}; + std::atomic<KThread*> m_current_thread{nullptr}; + + std::shared_ptr<Common::Fiber> m_switch_fiber{}; + KThread* m_switch_cur_thread{}; + KThread* m_switch_highest_priority_thread{}; + bool m_switch_from_schedule{}; }; -class [[nodiscard]] KScopedSchedulerLock : KScopedLock<GlobalSchedulerContext::LockType> { +class KScopedSchedulerLock : public KScopedLock<KScheduler::LockType> { public: - explicit KScopedSchedulerLock(KernelCore& kernel); - ~KScopedSchedulerLock(); + explicit KScopedSchedulerLock(KernelCore& kernel) + : KScopedLock(kernel.GlobalSchedulerContext().scheduler_lock) {} + ~KScopedSchedulerLock() = default; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h index 93c47f1b1..73314b45e 100644 --- a/src/core/hle/kernel/k_scheduler_lock.h +++ b/src/core/hle/kernel/k_scheduler_lock.h @@ -1,13 +1,15 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include <atomic> #include "common/assert.h" +#include "core/hle/kernel/k_interrupt_manager.h" #include "core/hle/kernel/k_spin_lock.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/physical_core.h" namespace Kernel { @@ -75,7 +77,7 @@ private: KernelCore& kernel; KAlignedSpinLock spin_lock{}; s32 lock_count{}; - KThread* owner_thread{}; + std::atomic<KThread*> owner_thread{}; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_scoped_lock.h b/src/core/hle/kernel/k_scoped_lock.h index 89a7ffe49..857e21156 100644 --- a/src/core/hle/kernel/k_scoped_lock.h +++ b/src/core/hle/kernel/k_scoped_lock.h @@ -1,9 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_scoped_resource_reservation.h b/src/core/hle/kernel/k_scoped_resource_reservation.h index 07272075d..436bcf9fe 100644 --- a/src/core/hle/kernel/k_scoped_resource_reservation.h +++ b/src/core/hle/kernel/k_scoped_resource_reservation.h @@ -1,9 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h index 2995c492d..76c095e69 100644 --- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h +++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h @@ -1,9 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp index 433fc98e1..e968f26ad 100644 --- a/src/core/hle/kernel/k_server_port.cpp +++ b/src/core/hle/kernel/k_server_port.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <tuple> #include "common/assert.h" @@ -62,6 +61,12 @@ void KServerPort::Destroy() { // Close our reference to our parent. parent->Close(); + + // Release host emulation members. + session_handler.reset(); + + // Ensure that the global list tracking server objects does not hold on to a reference. + kernel.UnregisterServerObject(this); } bool KServerPort::IsSignaled() const { diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h index 6302d5e61..fd4f4bd20 100644 --- a/src/core/hle/kernel/k_server_port.h +++ b/src/core/hle/kernel/k_server_port.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -30,11 +29,11 @@ public: /// Whether or not this server port has an HLE handler available. bool HasSessionRequestHandler() const { - return session_handler != nullptr; + return !session_handler.expired(); } /// Gets the HLE handler for this port. - SessionRequestHandlerPtr GetSessionRequestHandler() const { + SessionRequestHandlerWeakPtr GetSessionRequestHandler() const { return session_handler; } @@ -42,7 +41,7 @@ public: * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port * will inherit a reference to this handler. */ - void SetSessionHandler(SessionRequestHandlerPtr&& handler) { + void SetSessionHandler(SessionRequestHandlerWeakPtr&& handler) { session_handler = std::move(handler); } @@ -66,7 +65,7 @@ private: void CleanupSessions(); SessionList session_list; - SessionRequestHandlerPtr session_handler; + SessionRequestHandlerWeakPtr session_handler; KPort* parent{}; }; diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index 4d94eb9cf..802c646a6 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <tuple> #include <utility> @@ -27,10 +26,7 @@ namespace Kernel { KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} -KServerSession::~KServerSession() { - // Ensure that the global list tracking server sessions does not hold on to a reference. - kernel.UnregisterServerSession(this); -} +KServerSession::~KServerSession() = default; void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, std::shared_ptr<SessionRequestManager> manager_) { @@ -49,6 +45,12 @@ void KServerSession::Destroy() { parent->OnServerClosed(); parent->Close(); + + // Release host emulation members. + manager.reset(); + + // Ensure that the global list tracking server objects does not hold on to a reference. + kernel.UnregisterServerObject(this); } void KServerSession::OnClientClosed() { @@ -77,7 +79,7 @@ std::size_t KServerSession::NumDomainRequestHandlers() const { return manager->DomainHandlerCount(); } -ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { +Result KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { if (!context.HasDomainMessageHeader()) { return ResultSuccess; } @@ -95,10 +97,15 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co "object_id {} is too big! This probably means a recent service call " "to {} needed to return a new interface!", object_id, name); - UNREACHABLE(); + ASSERT(false); return ResultSuccess; // Ignore error if asserts are off } - return manager->DomainHandler(object_id - 1)->HandleSyncRequest(*this, context); + if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) { + return strong_ptr->HandleSyncRequest(*this, context); + } else { + ASSERT(false); + return ResultSuccess; + } case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id); @@ -116,7 +123,7 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co return ResultSuccess; } -ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) { +Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) { u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))}; auto context = std::make_shared<HLERequestContext>(kernel, memory, this, thread); @@ -136,8 +143,8 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor return ResultSuccess; } -ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) { - ResultCode result = ResultSuccess; +Result KServerSession::CompleteSyncRequest(HLERequestContext& context) { + Result result = ResultSuccess; // If the session has been converted to a domain, handle the domain request if (manager->HasSessionRequestHandler(context)) { @@ -166,8 +173,8 @@ ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) { return result; } -ResultCode KServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory, - Core::Timing::CoreTiming& core_timing) { +Result KServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory, + Core::Timing::CoreTiming& core_timing) { return QueueSyncRequest(thread, memory); } diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h index 5b76bf17c..6d0821945 100644 --- a/src/core/hle/kernel/k_server_session.h +++ b/src/core/hle/kernel/k_server_session.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -74,10 +73,10 @@ public: * @param memory Memory context to handle the sync request under. * @param core_timing Core timing context to schedule the request event under. * - * @returns ResultCode from the operation. + * @returns Result from the operation. */ - ResultCode HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory, - Core::Timing::CoreTiming& core_timing); + Result HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory, + Core::Timing::CoreTiming& core_timing); /// Adds a new domain request handler to the collection of request handlers within /// this ServerSession instance. @@ -104,14 +103,14 @@ public: private: /// Queues a sync request from the emulated application. - ResultCode QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory); + Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory); /// Completes a sync request from the emulated application. - ResultCode CompleteSyncRequest(HLERequestContext& context); + Result CompleteSyncRequest(HLERequestContext& context); /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an /// object handle. - ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context); + Result HandleDomainSyncRequest(Kernel::HLERequestContext& context); /// This session's HLE request handlers std::shared_ptr<SessionRequestManager> manager; diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp index a64b56b9e..ee05aa282 100644 --- a/src/core/hle/kernel/k_session.cpp +++ b/src/core/hle/kernel/k_session.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_client_port.h" #include "core/hle/kernel/k_client_session.h" diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h index 62c328a68..c6ead403b 100644 --- a/src/core/hle/kernel/k_session.h +++ b/src/core/hle/kernel/k_session.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp index 51d7538ca..8ff1545b6 100644 --- a/src/core/hle/kernel/k_shared_memory.cpp +++ b/src/core/hle/kernel/k_shared_memory.cpp @@ -1,6 +1,5 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "core/core.h" @@ -18,12 +17,10 @@ KSharedMemory::~KSharedMemory() { kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size); } -ResultCode KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, - KPageLinkedList&& page_list_, - Svc::MemoryPermission owner_permission_, - Svc::MemoryPermission user_permission_, - PAddr physical_address_, std::size_t size_, - std::string name_) { +Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, + KPageGroup&& page_list_, Svc::MemoryPermission owner_permission_, + Svc::MemoryPermission user_permission_, PAddr physical_address_, + std::size_t size_, std::string name_) { // Set members. owner_process = owner_process_; device_memory = &device_memory_; @@ -67,8 +64,8 @@ void KSharedMemory::Finalize() { KAutoObjectWithSlabHeapAndContainer<KSharedMemory, KAutoObjectWithList>::Finalize(); } -ResultCode KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t map_size, - Svc::MemoryPermission permissions) { +Result KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t map_size, + Svc::MemoryPermission permissions) { const u64 page_count{(map_size + PageSize - 1) / PageSize}; if (page_list.GetNumPages() != page_count) { @@ -86,7 +83,7 @@ ResultCode KSharedMemory::Map(KProcess& target_process, VAddr address, std::size ConvertToKMemoryPermission(permissions)); } -ResultCode KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) { +Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) { const u64 page_count{(unmap_size + PageSize - 1) / PageSize}; if (page_list.GetNumPages() != page_count) { diff --git a/src/core/hle/kernel/k_shared_memory.h b/src/core/hle/kernel/k_shared_memory.h index 81de36136..34cb98456 100644 --- a/src/core/hle/kernel/k_shared_memory.h +++ b/src/core/hle/kernel/k_shared_memory.h @@ -1,6 +1,5 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -9,7 +8,7 @@ #include "common/common_types.h" #include "core/device_memory.h" #include "core/hle/kernel/k_memory_block.h" -#include "core/hle/kernel/k_page_linked_list.h" +#include "core/hle/kernel/k_page_group.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/slab_helpers.h" #include "core/hle/result.h" @@ -26,10 +25,10 @@ public: explicit KSharedMemory(KernelCore& kernel_); ~KSharedMemory() override; - ResultCode Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, - KPageLinkedList&& page_list_, Svc::MemoryPermission owner_permission_, - Svc::MemoryPermission user_permission_, PAddr physical_address_, - std::size_t size_, std::string name_); + Result Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, + KPageGroup&& page_list_, Svc::MemoryPermission owner_permission_, + Svc::MemoryPermission user_permission_, PAddr physical_address_, + std::size_t size_, std::string name_); /** * Maps a shared memory block to an address in the target process' address space @@ -38,8 +37,8 @@ public: * @param map_size Size of the shared memory block to map * @param permissions Memory block map permissions (specified by SVC field) */ - ResultCode Map(KProcess& target_process, VAddr address, std::size_t map_size, - Svc::MemoryPermission permissions); + Result Map(KProcess& target_process, VAddr address, std::size_t map_size, + Svc::MemoryPermission permissions); /** * Unmaps a shared memory block from an address in the target process' address space @@ -47,7 +46,7 @@ public: * @param address Address in system memory to unmap shared memory block * @param unmap_size Size of the shared memory block to unmap */ - ResultCode Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size); + Result Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size); /** * Gets a pointer to the shared memory block @@ -77,7 +76,7 @@ public: private: Core::DeviceMemory* device_memory; KProcess* owner_process{}; - KPageLinkedList page_list; + KPageGroup page_list; Svc::MemoryPermission owner_permission{}; Svc::MemoryPermission user_permission{}; PAddr physical_address{}; diff --git a/src/core/hle/kernel/k_shared_memory_info.h b/src/core/hle/kernel/k_shared_memory_info.h index 20bc19f46..e43db8515 100644 --- a/src/core/hle/kernel/k_shared_memory_info.h +++ b/src/core/hle/kernel/k_shared_memory_info.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_slab_heap.h b/src/core/hle/kernel/k_slab_heap.h index 05c0bec9c..2b303537e 100644 --- a/src/core/hle/kernel/k_slab_heap.h +++ b/src/core/hle/kernel/k_slab_heap.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -16,39 +15,34 @@ class KernelCore; namespace impl { -class KSlabHeapImpl final { -public: +class KSlabHeapImpl { YUZU_NON_COPYABLE(KSlabHeapImpl); YUZU_NON_MOVEABLE(KSlabHeapImpl); +public: struct Node { Node* next{}; }; +public: constexpr KSlabHeapImpl() = default; - constexpr ~KSlabHeapImpl() = default; - void Initialize(std::size_t size) { - ASSERT(head == nullptr); - obj_size = size; - } - - constexpr std::size_t GetObjectSize() const { - return obj_size; + void Initialize() { + ASSERT(m_head == nullptr); } Node* GetHead() const { - return head; + return m_head; } void* Allocate() { - Node* ret = head.load(); + Node* ret = m_head.load(); do { if (ret == nullptr) { break; } - } while (!head.compare_exchange_weak(ret, ret->next)); + } while (!m_head.compare_exchange_weak(ret, ret->next)); return ret; } @@ -56,170 +50,157 @@ public: void Free(void* obj) { Node* node = static_cast<Node*>(obj); - Node* cur_head = head.load(); + Node* cur_head = m_head.load(); do { node->next = cur_head; - } while (!head.compare_exchange_weak(cur_head, node)); + } while (!m_head.compare_exchange_weak(cur_head, node)); } private: - std::atomic<Node*> head{}; - std::size_t obj_size{}; + std::atomic<Node*> m_head{}; }; } // namespace impl -class KSlabHeapBase { -public: +template <bool SupportDynamicExpansion> +class KSlabHeapBase : protected impl::KSlabHeapImpl { YUZU_NON_COPYABLE(KSlabHeapBase); YUZU_NON_MOVEABLE(KSlabHeapBase); - constexpr KSlabHeapBase() = default; - constexpr ~KSlabHeapBase() = default; +private: + size_t m_obj_size{}; + uintptr_t m_peak{}; + uintptr_t m_start{}; + uintptr_t m_end{}; - constexpr bool Contains(uintptr_t addr) const { - return start <= addr && addr < end; - } +private: + void UpdatePeakImpl(uintptr_t obj) { + static_assert(std::atomic_ref<uintptr_t>::is_always_lock_free); + std::atomic_ref<uintptr_t> peak_ref(m_peak); - constexpr std::size_t GetSlabHeapSize() const { - return (end - start) / GetObjectSize(); + const uintptr_t alloc_peak = obj + this->GetObjectSize(); + uintptr_t cur_peak = m_peak; + do { + if (alloc_peak <= cur_peak) { + break; + } + } while (!peak_ref.compare_exchange_strong(cur_peak, alloc_peak)); } - constexpr std::size_t GetObjectSize() const { - return impl.GetObjectSize(); - } +public: + constexpr KSlabHeapBase() = default; - constexpr uintptr_t GetSlabHeapAddress() const { - return start; + bool Contains(uintptr_t address) const { + return m_start <= address && address < m_end; } - std::size_t GetObjectIndexImpl(const void* obj) const { - return (reinterpret_cast<uintptr_t>(obj) - start) / GetObjectSize(); + void Initialize(size_t obj_size, void* memory, size_t memory_size) { + // Ensure we don't initialize a slab using null memory. + ASSERT(memory != nullptr); + + // Set our object size. + m_obj_size = obj_size; + + // Initialize the base allocator. + KSlabHeapImpl::Initialize(); + + // Set our tracking variables. + const size_t num_obj = (memory_size / obj_size); + m_start = reinterpret_cast<uintptr_t>(memory); + m_end = m_start + num_obj * obj_size; + m_peak = m_start; + + // Free the objects. + u8* cur = reinterpret_cast<u8*>(m_end); + + for (size_t i = 0; i < num_obj; i++) { + cur -= obj_size; + KSlabHeapImpl::Free(cur); + } } - std::size_t GetPeakIndex() const { - return GetObjectIndexImpl(reinterpret_cast<const void*>(peak)); + size_t GetSlabHeapSize() const { + return (m_end - m_start) / this->GetObjectSize(); } - void* AllocateImpl() { - return impl.Allocate(); + size_t GetObjectSize() const { + return m_obj_size; } - void FreeImpl(void* obj) { - // Don't allow freeing an object that wasn't allocated from this heap - ASSERT(Contains(reinterpret_cast<uintptr_t>(obj))); + void* Allocate() { + void* obj = KSlabHeapImpl::Allocate(); - impl.Free(obj); + return obj; } - void InitializeImpl(std::size_t obj_size, void* memory, std::size_t memory_size) { - // Ensure we don't initialize a slab using null memory - ASSERT(memory != nullptr); - - // Initialize the base allocator - impl.Initialize(obj_size); + void Free(void* obj) { + // Don't allow freeing an object that wasn't allocated from this heap. + const bool contained = this->Contains(reinterpret_cast<uintptr_t>(obj)); + ASSERT(contained); + KSlabHeapImpl::Free(obj); + } - // Set our tracking variables - const std::size_t num_obj = (memory_size / obj_size); - start = reinterpret_cast<uintptr_t>(memory); - end = start + num_obj * obj_size; - peak = start; + size_t GetObjectIndex(const void* obj) const { + if constexpr (SupportDynamicExpansion) { + if (!this->Contains(reinterpret_cast<uintptr_t>(obj))) { + return std::numeric_limits<size_t>::max(); + } + } - // Free the objects - u8* cur = reinterpret_cast<u8*>(end); + return (reinterpret_cast<uintptr_t>(obj) - m_start) / this->GetObjectSize(); + } - for (std::size_t i{}; i < num_obj; i++) { - cur -= obj_size; - impl.Free(cur); - } + size_t GetPeakIndex() const { + return this->GetObjectIndex(reinterpret_cast<const void*>(m_peak)); } -private: - using Impl = impl::KSlabHeapImpl; + uintptr_t GetSlabHeapAddress() const { + return m_start; + } - Impl impl; - uintptr_t peak{}; - uintptr_t start{}; - uintptr_t end{}; + size_t GetNumRemaining() const { + // Only calculate the number of remaining objects under debug configuration. + return 0; + } }; template <typename T> -class KSlabHeap final : public KSlabHeapBase { -public: - enum class AllocationType { - Host, - Guest, - }; +class KSlabHeap final : public KSlabHeapBase<false> { +private: + using BaseHeap = KSlabHeapBase<false>; - explicit constexpr KSlabHeap(AllocationType allocation_type_ = AllocationType::Host) - : KSlabHeapBase(), allocation_type{allocation_type_} {} +public: + constexpr KSlabHeap() = default; - void Initialize(void* memory, std::size_t memory_size) { - if (allocation_type == AllocationType::Guest) { - InitializeImpl(sizeof(T), memory, memory_size); - } + void Initialize(void* memory, size_t memory_size) { + BaseHeap::Initialize(sizeof(T), memory, memory_size); } T* Allocate() { - switch (allocation_type) { - case AllocationType::Host: - // Fallback for cases where we do not yet support allocating guest memory from the slab - // heap, such as for kernel memory regions. - return new T; - - case AllocationType::Guest: - T* obj = static_cast<T*>(AllocateImpl()); - if (obj != nullptr) { - new (obj) T(); - } - return obj; - } + T* obj = static_cast<T*>(BaseHeap::Allocate()); - UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); - return nullptr; + if (obj != nullptr) [[likely]] { + std::construct_at(obj); + } + return obj; } - T* AllocateWithKernel(KernelCore& kernel) { - switch (allocation_type) { - case AllocationType::Host: - // Fallback for cases where we do not yet support allocating guest memory from the slab - // heap, such as for kernel memory regions. - return new T(kernel); + T* Allocate(KernelCore& kernel) { + T* obj = static_cast<T*>(BaseHeap::Allocate()); - case AllocationType::Guest: - T* obj = static_cast<T*>(AllocateImpl()); - if (obj != nullptr) { - new (obj) T(kernel); - } - return obj; + if (obj != nullptr) [[likely]] { + std::construct_at(obj, kernel); } - - UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); - return nullptr; + return obj; } void Free(T* obj) { - switch (allocation_type) { - case AllocationType::Host: - // Fallback for cases where we do not yet support allocating guest memory from the slab - // heap, such as for kernel memory regions. - delete obj; - return; - - case AllocationType::Guest: - FreeImpl(obj); - return; - } - - UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); + BaseHeap::Free(obj); } - constexpr std::size_t GetObjectIndex(const T* obj) const { - return GetObjectIndexImpl(obj); + size_t GetObjectIndex(const T* obj) const { + return BaseHeap::GetObjectIndex(obj); } - -private: - const AllocationType allocation_type; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_spin_lock.cpp b/src/core/hle/kernel/k_spin_lock.cpp index 4412aa4bb..6e16a1849 100644 --- a/src/core/hle/kernel/k_spin_lock.cpp +++ b/src/core/hle/kernel/k_spin_lock.cpp @@ -1,54 +1,20 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_spin_lock.h" -#if _MSC_VER -#include <intrin.h> -#if _M_AMD64 -#define __x86_64__ 1 -#endif -#if _M_ARM64 -#define __aarch64__ 1 -#endif -#else -#if __x86_64__ -#include <xmmintrin.h> -#endif -#endif - -namespace { - -void ThreadPause() { -#if __x86_64__ - _mm_pause(); -#elif __aarch64__ && _MSC_VER - __yield(); -#elif __aarch64__ - asm("yield"); -#endif -} - -} // namespace - namespace Kernel { void KSpinLock::Lock() { - while (lck.test_and_set(std::memory_order_acquire)) { - ThreadPause(); - } + lck.lock(); } void KSpinLock::Unlock() { - lck.clear(std::memory_order_release); + lck.unlock(); } bool KSpinLock::TryLock() { - if (lck.test_and_set(std::memory_order_acquire)) { - return false; - } - return true; + return lck.try_lock(); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_spin_lock.h b/src/core/hle/kernel/k_spin_lock.h index 4d87d006a..397a93d21 100644 --- a/src/core/hle/kernel/k_spin_lock.h +++ b/src/core/hle/kernel/k_spin_lock.h @@ -1,10 +1,9 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <atomic> +#include <mutex> #include "core/hle/kernel/k_scoped_lock.h" @@ -25,7 +24,7 @@ public: [[nodiscard]] bool TryLock(); private: - std::atomic_flag lck = ATOMIC_FLAG_INIT; + std::mutex lck; }; // TODO(bunnei): Alias for now, in case we want to implement these accurately in the future. diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp index e4c5eb74f..802dca046 100644 --- a/src/core/hle/kernel/k_synchronization_object.cpp +++ b/src/core/hle/kernel/k_synchronization_object.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/common_types.h" @@ -23,7 +22,7 @@ public: : KThreadQueueWithoutEndWait(kernel_), m_objects(o), m_nodes(n), m_count(c) {} void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object, - ResultCode wait_result) override { + Result wait_result) override { // Determine the sync index, and unlink all nodes. s32 sync_index = -1; for (auto i = 0; i < m_count; ++i) { @@ -46,8 +45,7 @@ public: KThreadQueue::EndWait(waiting_thread, wait_result); } - void CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task) override { + void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Remove all nodes from our list. for (auto i = 0; i < m_count; ++i) { m_objects[i]->UnlinkNode(std::addressof(m_nodes[i])); @@ -73,9 +71,9 @@ void KSynchronizationObject::Finalize() { KAutoObject::Finalize(); } -ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index, - KSynchronizationObject** objects, const s32 num_objects, - s64 timeout) { +Result KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index, + KSynchronizationObject** objects, const s32 num_objects, + s64 timeout) { // Allocate space on stack for thread nodes. std::vector<ThreadListNode> thread_nodes(num_objects); @@ -149,7 +147,7 @@ KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_) KSynchronizationObject::~KSynchronizationObject() = default; -void KSynchronizationObject::NotifyAvailable(ResultCode result) { +void KSynchronizationObject::NotifyAvailable(Result result) { KScopedSchedulerLock sl(kernel); // If we're not signaled, we've nothing to notify. diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h index ec235437b..8d8122ab7 100644 --- a/src/core/hle/kernel/k_synchronization_object.h +++ b/src/core/hle/kernel/k_synchronization_object.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -25,9 +24,9 @@ public: KThread* thread{}; }; - [[nodiscard]] static ResultCode Wait(KernelCore& kernel, s32* out_index, - KSynchronizationObject** objects, const s32 num_objects, - s64 timeout); + [[nodiscard]] static Result Wait(KernelCore& kernel, s32* out_index, + KSynchronizationObject** objects, const s32 num_objects, + s64 timeout); void Finalize() override; @@ -73,7 +72,7 @@ protected: virtual void OnFinalizeSynchronizationObject() {} - void NotifyAvailable(ResultCode result); + void NotifyAvailable(Result result); void NotifyAvailable() { return this->NotifyAvailable(ResultSuccess); } diff --git a/src/core/hle/kernel/k_system_control.h b/src/core/hle/kernel/k_system_control.h index d755082c2..b8292e9d0 100644 --- a/src/core/hle/kernel/k_system_control.h +++ b/src/core/hle/kernel/k_system_control.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index de3ffe0c7..174afc80d 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <atomic> @@ -14,9 +13,7 @@ #include "common/common_types.h" #include "common/fiber.h" #include "common/logging/log.h" -#include "common/scope_exit.h" #include "common/settings.h" -#include "common/thread_queue_list.h" #include "core/core.h" #include "core/cpu_manager.h" #include "core/hardware_properties.h" @@ -33,7 +30,6 @@ #include "core/hle/kernel/k_worker_task_manager.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/svc_results.h" -#include "core/hle/kernel/time_manager.h" #include "core/hle/result.h" #include "core/memory.h" @@ -84,8 +80,7 @@ public: explicit ThreadQueueImplForKThreadSetProperty(KernelCore& kernel_, KThread::WaiterList* wl) : KThreadQueue(kernel_), m_wait_list(wl) {} - void CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task) override { + void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Remove the thread from the wait list. m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread)); @@ -103,8 +98,8 @@ KThread::KThread(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_}, activity_pause_lock{kernel_} {} KThread::~KThread() = default; -ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio, - s32 virt_core, KProcess* owner, ThreadType type) { +Result KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio, + s32 virt_core, KProcess* owner, ThreadType type) { // Assert parameters are valid. ASSERT((type == ThreadType::Main) || (type == ThreadType::Dummy) || (Svc::HighestThreadPriority <= prio && prio <= Svc::LowestThreadPriority)); @@ -137,7 +132,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s UNIMPLEMENTED(); break; default: - UNREACHABLE_MSG("KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type)); + ASSERT_MSG(false, "KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type)); break; } thread_type = type; @@ -202,6 +197,10 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s resource_limit_release_hint = false; cpu_time = 0; + // Set debug context. + stack_top = user_stack_top; + argument = arg; + // Clear our stack parameters. std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0, sizeof(StackParameters)); @@ -210,7 +209,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s if (owner != nullptr) { // Setup the TLS, if needed. if (type == ThreadType::User) { - tls_address = owner->CreateTLSRegion(); + R_TRY(owner->CreateThreadLocalRegion(std::addressof(tls_address))); } parent = owner; @@ -225,7 +224,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s // Setup the stack parameters. StackParameters& sp = GetStackParameters(); sp.cur_thread = this; - sp.disable_count = 0; + sp.disable_count = 1; SetInExceptionHandler(); // Set thread ID. @@ -245,46 +244,51 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s return ResultSuccess; } -ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, - VAddr user_stack_top, s32 prio, s32 core, KProcess* owner, - ThreadType type, std::function<void(void*)>&& init_func, - void* init_func_parameter) { +Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, + VAddr user_stack_top, s32 prio, s32 core, KProcess* owner, + ThreadType type, std::function<void()>&& init_func) { // Initialize the thread. R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); // Initialize emulation parameters. - thread->host_context = - std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter); + thread->host_context = std::make_shared<Common::Fiber>(std::move(init_func)); thread->is_single_core = !Settings::values.use_multi_core.GetValue(); return ResultSuccess; } -ResultCode KThread::InitializeDummyThread(KThread* thread) { - return thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy); +Result KThread::InitializeDummyThread(KThread* thread) { + // Initialize the thread. + R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy)); + + // Initialize emulation parameters. + thread->stack_parameters.disable_count = 0; + + return ResultSuccess; +} + +Result KThread::InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core) { + return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main, + system.GetCpuManager().GetGuestActivateFunc()); } -ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { +Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main, - Core::CpuManager::GetIdleThreadStartFunc(), - system.GetCpuManager().GetStartFuncParamater()); + system.GetCpuManager().GetIdleThreadStartFunc()); } -ResultCode KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread, - KThreadFunction func, uintptr_t arg, - s32 virt_core) { +Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread, + KThreadFunction func, uintptr_t arg, s32 virt_core) { return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority, - Core::CpuManager::GetSuspendThreadStartFunc(), - system.GetCpuManager().GetStartFuncParamater()); + system.GetCpuManager().GetShutdownThreadStartFunc()); } -ResultCode KThread::InitializeUserThread(Core::System& system, KThread* thread, - KThreadFunction func, uintptr_t arg, VAddr user_stack_top, - s32 prio, s32 virt_core, KProcess* owner) { +Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func, + uintptr_t arg, VAddr user_stack_top, s32 prio, s32 virt_core, + KProcess* owner) { system.Kernel().GlobalSchedulerContext().AddThread(thread); return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner, - ThreadType::User, Core::CpuManager::GetGuestThreadStartFunc(), - system.GetCpuManager().GetStartFuncParamater()); + ThreadType::User, system.GetCpuManager().GetGuestThreadFunc()); } void KThread::PostDestroy(uintptr_t arg) { @@ -305,7 +309,7 @@ void KThread::Finalize() { // If the thread has a local region, delete it. if (tls_address != 0) { - parent->FreeTLSRegion(tls_address); + ASSERT(parent->DeleteThreadLocalRegion(tls_address).IsSuccess()); } // Release any waiters. @@ -315,17 +319,26 @@ void KThread::Finalize() { auto it = waiter_list.begin(); while (it != waiter_list.end()) { - // Clear the lock owner - it->SetLockOwner(nullptr); + // Get the thread. + KThread* const waiter = std::addressof(*it); + + // The thread shouldn't be a kernel waiter. + ASSERT(!IsKernelAddressKey(waiter->GetAddressKey())); + + // Clear the lock owner. + waiter->SetLockOwner(nullptr); // Erase the waiter from our list. it = waiter_list.erase(it); // Cancel the thread's wait. - it->CancelWait(ResultInvalidState, true); + waiter->CancelWait(ResultInvalidState, true); } } + // Release host emulation members. + host_context.reset(); + // Perform inherited finalization. KSynchronizationObject::Finalize(); } @@ -379,7 +392,7 @@ void KThread::FinishTermination() { for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) { KThread* core_thread{}; do { - core_thread = kernel.Scheduler(i).GetCurrentThread(); + core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread(); } while (core_thread == this); } } @@ -484,9 +497,7 @@ void KThread::Unpin() { // Resume any threads that began waiting on us while we were pinned. for (auto it = pinned_waiter_list.begin(); it != pinned_waiter_list.end(); ++it) { - if (it->GetState() == ThreadState::Waiting) { - it->SetState(ThreadState::Runnable); - } + it->EndWait(ResultSuccess); } } @@ -520,7 +531,7 @@ void KThread::ClearInterruptFlag() { memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 0); } -ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { +Result KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { KScopedSchedulerLock sl{kernel}; // Get the virtual mask. @@ -530,7 +541,7 @@ ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { return ResultSuccess; } -ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { +Result KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { KScopedSchedulerLock sl{kernel}; ASSERT(num_core_migration_disables >= 0); @@ -546,7 +557,7 @@ ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_m return ResultSuccess; } -ResultCode KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) { +Result KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) { ASSERT(parent != nullptr); ASSERT(v_affinity_mask != 0); KScopedLightLock lk(activity_pause_lock); @@ -628,7 +639,7 @@ ResultCode KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) { s32 thread_core; for (thread_core = 0; thread_core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++thread_core) { - if (kernel.Scheduler(thread_core).GetCurrentThread() == this) { + if (kernel.Scheduler(thread_core).GetSchedulerCurrentThread() == this) { thread_is_current = true; break; } @@ -723,10 +734,10 @@ void KThread::UpdateState() { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); // Set our suspend flags in state. - const auto old_state = thread_state; + const ThreadState old_state = thread_state.load(std::memory_order_relaxed); const auto new_state = static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask); - thread_state = new_state; + thread_state.store(new_state, std::memory_order_relaxed); // Note the state change in scheduler. if (new_state != old_state) { @@ -738,14 +749,27 @@ void KThread::Continue() { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); // Clear our suspend flags in state. - const auto old_state = thread_state; - thread_state = old_state & ThreadState::Mask; + const ThreadState old_state = thread_state.load(std::memory_order_relaxed); + thread_state.store(old_state & ThreadState::Mask, std::memory_order_relaxed); // Note the state change in scheduler. KScheduler::OnThreadStateChanged(kernel, this, old_state); } -ResultCode KThread::SetActivity(Svc::ThreadActivity activity) { +void KThread::WaitUntilSuspended() { + // Make sure we have a suspend requested. + ASSERT(IsSuspendRequested()); + + // Loop until the thread is not executing on any core. + for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) { + KThread* core_thread{}; + do { + core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread(); + } while (core_thread == this); + } +} + +Result KThread::SetActivity(Svc::ThreadActivity activity) { // Lock ourselves. KScopedLightLock lk(activity_pause_lock); @@ -806,7 +830,7 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) { // Check if the thread is currently running. // If it is, we'll need to retry. for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { - if (kernel.Scheduler(i).GetCurrentThread() == this) { + if (kernel.Scheduler(i).GetSchedulerCurrentThread() == this) { thread_is_current = true; break; } @@ -818,7 +842,7 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) { return ResultSuccess; } -ResultCode KThread::GetThreadContext3(std::vector<u8>& out) { +Result KThread::GetThreadContext3(std::vector<u8>& out) { // Lock ourselves. KScopedLightLock lk{activity_pause_lock}; @@ -868,6 +892,7 @@ void KThread::AddWaiterImpl(KThread* thread) { // Keep track of how many kernel waiters we have. if (IsKernelAddressKey(thread->GetAddressKey())) { ASSERT((num_kernel_waiters++) >= 0); + KScheduler::SetSchedulerUpdateNeeded(kernel); } // Insert the waiter. @@ -881,6 +906,7 @@ void KThread::RemoveWaiterImpl(KThread* thread) { // Keep track of how many kernel waiters we have. if (IsKernelAddressKey(thread->GetAddressKey())) { ASSERT((num_kernel_waiters--) > 0); + KScheduler::SetSchedulerUpdateNeeded(kernel); } // Remove the waiter. @@ -956,6 +982,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) { // Keep track of how many kernel waiters we have. if (IsKernelAddressKey(thread->GetAddressKey())) { ASSERT((num_kernel_waiters--) > 0); + KScheduler::SetSchedulerUpdateNeeded(kernel); } it = waiter_list.erase(it); @@ -983,7 +1010,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) { return next_lock_owner; } -ResultCode KThread::Run() { +Result KThread::Run() { while (true) { KScopedSchedulerLock lk{kernel}; @@ -1011,8 +1038,6 @@ ResultCode KThread::Run() { // Set our state and finish. SetState(ThreadState::Runnable); - DisableDispatch(); - return ResultSuccess; } } @@ -1044,9 +1069,11 @@ void KThread::Exit() { // Register the thread as a work task. KWorkerTaskManager::AddTask(kernel, KWorkerTaskManager::WorkerType::Exit, this); } + + UNREACHABLE_MSG("KThread::Exit() would return"); } -ResultCode KThread::Sleep(s64 timeout) { +Result KThread::Sleep(s64 timeout) { ASSERT(!kernel.GlobalSchedulerContext().IsLocked()); ASSERT(this == GetCurrentThreadPointer(kernel)); ASSERT(timeout > 0); @@ -1079,17 +1106,12 @@ void KThread::IfDummyThreadTryWait() { return; } - // Block until we can grab the lock. - KScopedSpinLock lk{dummy_wait_lock}; -} - -void KThread::IfDummyThreadBeginWait() { - if (!IsDummyThread()) { - return; - } + ASSERT(!kernel.IsPhantomModeForSingleCore()); - // Ensure the thread will block when IfDummyThreadTryWait is called. - dummy_wait_lock.Lock(); + // Block until we are no longer waiting. + std::unique_lock lk(dummy_wait_lock); + dummy_wait_cv.wait( + lk, [&] { return GetState() != ThreadState::Waiting || kernel.IsShuttingDown(); }); } void KThread::IfDummyThreadEndWait() { @@ -1097,8 +1119,8 @@ void KThread::IfDummyThreadEndWait() { return; } - // Ensure the thread will no longer block. - dummy_wait_lock.Unlock(); + // Wake up the waiting thread. + dummy_wait_cv.notify_one(); } void KThread::BeginWait(KThreadQueue* queue) { @@ -1107,12 +1129,9 @@ void KThread::BeginWait(KThreadQueue* queue) { // Set our wait queue. wait_queue = queue; - - // Special case for dummy threads to ensure they block. - IfDummyThreadBeginWait(); } -void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) { +void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, Result wait_result_) { // Lock the scheduler. KScopedSchedulerLock sl(kernel); @@ -1122,7 +1141,7 @@ void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCod } } -void KThread::EndWait(ResultCode wait_result_) { +void KThread::EndWait(Result wait_result_) { // Lock the scheduler. KScopedSchedulerLock sl(kernel); @@ -1141,7 +1160,7 @@ void KThread::EndWait(ResultCode wait_result_) { } } -void KThread::CancelWait(ResultCode wait_result_, bool cancel_timer_task) { +void KThread::CancelWait(Result wait_result_, bool cancel_timer_task) { // Lock the scheduler. KScopedSchedulerLock sl(kernel); @@ -1158,10 +1177,11 @@ void KThread::SetState(ThreadState state) { SetMutexWaitAddressForDebugging({}); SetWaitReasonForDebugging({}); - const ThreadState old_state = thread_state; - thread_state = - static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)); - if (thread_state != old_state) { + const ThreadState old_state = thread_state.load(std::memory_order_relaxed); + thread_state.store( + static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)), + std::memory_order_relaxed); + if (thread_state.load(std::memory_order_relaxed) != old_state) { KScheduler::OnThreadStateChanged(kernel, this, old_state); } } @@ -1170,6 +1190,10 @@ std::shared_ptr<Common::Fiber>& KThread::GetHostContext() { return host_context; } +void SetCurrentThread(KernelCore& kernel, KThread* thread) { + kernel.SetCurrentEmuThread(thread); +} + KThread* GetCurrentThreadPointer(KernelCore& kernel) { return kernel.GetCurrentEmuThread(); } @@ -1188,16 +1212,13 @@ KScopedDisableDispatch::~KScopedDisableDispatch() { return; } - // Skip the reschedule if single-core, as dispatch tracking is disabled here. - if (!Settings::values.use_multi_core.GetValue()) { - return; - } - if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) { - auto scheduler = kernel.CurrentScheduler(); + auto* scheduler = kernel.CurrentScheduler(); - if (scheduler) { + if (scheduler && !kernel.IsPhantomModeForSingleCore()) { scheduler->RescheduleCurrentCore(); + } else { + KScheduler::RescheduleCurrentHLEThread(kernel); } } else { GetCurrentThread(kernel).EnableDispatch(); diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index d058db62c..9ee20208e 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -1,10 +1,12 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <array> +#include <atomic> +#include <condition_variable> +#include <mutex> #include <span> #include <string> #include <utility> @@ -14,6 +16,7 @@ #include "common/common_types.h" #include "common/intrusive_red_black_tree.h" +#include "common/spin_lock.h" #include "core/arm/arm_interface.h" #include "core/hle/kernel/k_affinity_mask.h" #include "core/hle/kernel/k_light_lock.h" @@ -97,6 +100,13 @@ enum class ThreadWaitReasonForDebugging : u32 { Suspended, ///< Thread is waiting due to process suspension }; +enum class StepState : u32 { + NotStepping, ///< Thread is not currently stepping + StepPending, ///< Thread will step when next scheduled + StepPerformed, ///< Thread has stepped, waiting to be scheduled again +}; + +void SetCurrentThread(KernelCore& kernel, KThread* thread); [[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel); [[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel); [[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel); @@ -166,7 +176,7 @@ public: void SetBasePriority(s32 value); - [[nodiscard]] ResultCode Run(); + [[nodiscard]] Result Run(); void Exit(); @@ -198,6 +208,8 @@ public: void Continue(); + void WaitUntilSuspended(); + constexpr void SetSyncedIndex(s32 index) { synced_index = index; } @@ -206,11 +218,11 @@ public: return synced_index; } - constexpr void SetWaitResult(ResultCode wait_res) { + constexpr void SetWaitResult(Result wait_res) { wait_result = wait_res; } - [[nodiscard]] constexpr ResultCode GetWaitResult() const { + [[nodiscard]] constexpr Result GetWaitResult() const { return wait_result; } @@ -255,15 +267,23 @@ public: [[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext(); [[nodiscard]] ThreadState GetState() const { - return thread_state & ThreadState::Mask; + return thread_state.load(std::memory_order_relaxed) & ThreadState::Mask; } [[nodiscard]] ThreadState GetRawState() const { - return thread_state; + return thread_state.load(std::memory_order_relaxed); } void SetState(ThreadState state); + [[nodiscard]] StepState GetStepState() const { + return step_state; + } + + void SetStepState(StepState state) { + step_state = state; + } + [[nodiscard]] s64 GetLastScheduledTick() const { return last_scheduled_tick; } @@ -325,15 +345,15 @@ public: return physical_affinity_mask; } - [[nodiscard]] ResultCode GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask); + [[nodiscard]] Result GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask); - [[nodiscard]] ResultCode GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask); + [[nodiscard]] Result GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask); - [[nodiscard]] ResultCode SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask); + [[nodiscard]] Result SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask); - [[nodiscard]] ResultCode SetActivity(Svc::ThreadActivity activity); + [[nodiscard]] Result SetActivity(Svc::ThreadActivity activity); - [[nodiscard]] ResultCode Sleep(s64 timeout); + [[nodiscard]] Result Sleep(s64 timeout); [[nodiscard]] s64 GetYieldScheduleCount() const { return schedule_count; @@ -391,20 +411,22 @@ public: static void PostDestroy(uintptr_t arg); - [[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread); + [[nodiscard]] static Result InitializeDummyThread(KThread* thread); + + [[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread, + s32 virt_core); - [[nodiscard]] static ResultCode InitializeIdleThread(Core::System& system, KThread* thread, - s32 virt_core); + [[nodiscard]] static Result InitializeIdleThread(Core::System& system, KThread* thread, + s32 virt_core); - [[nodiscard]] static ResultCode InitializeHighPriorityThread(Core::System& system, - KThread* thread, - KThreadFunction func, - uintptr_t arg, s32 virt_core); + [[nodiscard]] static Result InitializeHighPriorityThread(Core::System& system, KThread* thread, + KThreadFunction func, uintptr_t arg, + s32 virt_core); - [[nodiscard]] static ResultCode InitializeUserThread(Core::System& system, KThread* thread, - KThreadFunction func, uintptr_t arg, - VAddr user_stack_top, s32 prio, - s32 virt_core, KProcess* owner); + [[nodiscard]] static Result InitializeUserThread(Core::System& system, KThread* thread, + KThreadFunction func, uintptr_t arg, + VAddr user_stack_top, s32 prio, s32 virt_core, + KProcess* owner); public: struct StackParameters { @@ -461,39 +483,16 @@ public: return per_core_priority_queue_entry[core]; } - [[nodiscard]] bool IsKernelThread() const { - return GetActiveCore() == 3; - } - - [[nodiscard]] bool IsDispatchTrackingDisabled() const { - return is_single_core || IsKernelThread(); - } - [[nodiscard]] s32 GetDisableDispatchCount() const { - if (IsDispatchTrackingDisabled()) { - // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch. - return 1; - } - return this->GetStackParameters().disable_count; } void DisableDispatch() { - if (IsDispatchTrackingDisabled()) { - // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch. - return; - } - ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0); this->GetStackParameters().disable_count++; } void EnableDispatch() { - if (IsDispatchTrackingDisabled()) { - // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch. - return; - } - ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0); this->GetStackParameters().disable_count--; } @@ -590,7 +589,7 @@ public: void RemoveWaiter(KThread* thread); - [[nodiscard]] ResultCode GetThreadContext3(std::vector<u8>& out); + [[nodiscard]] Result GetThreadContext3(std::vector<u8>& out); [[nodiscard]] KThread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key); @@ -616,9 +615,9 @@ public: } void BeginWait(KThreadQueue* queue); - void NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_); - void EndWait(ResultCode wait_result_); - void CancelWait(ResultCode wait_result_, bool cancel_timer_task); + void NotifyAvailable(KSynchronizationObject* signaled_object, Result wait_result_); + void EndWait(Result wait_result_); + void CancelWait(Result wait_result_, bool cancel_timer_task); [[nodiscard]] bool HasWaiters() const { return !waiter_list.empty(); @@ -641,9 +640,16 @@ public: // blocking as needed. void IfDummyThreadTryWait(); - void IfDummyThreadBeginWait(); void IfDummyThreadEndWait(); + [[nodiscard]] uintptr_t GetArgument() const { + return argument; + } + + [[nodiscard]] VAddr GetUserStackTop() const { + return stack_top; + } + private: static constexpr size_t PriorityInheritanceCountMax = 10; union SyncObjectBuffer { @@ -656,7 +662,7 @@ private: static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles)); struct ConditionVariableComparator { - struct LightCompareType { + struct RedBlackKeyType { u64 cv_key{}; s32 priority{}; @@ -672,8 +678,8 @@ private: template <typename T> requires( std::same_as<T, KThread> || - std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs, - const KThread& rhs) { + std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs, + const KThread& rhs) { const u64 l_key = lhs.GetConditionVariableKey(); const u64 r_key = rhs.GetConditionVariableKey(); @@ -697,14 +703,13 @@ private: void FinishTermination(); - [[nodiscard]] ResultCode Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, - s32 prio, s32 virt_core, KProcess* owner, ThreadType type); + [[nodiscard]] Result Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, + s32 prio, s32 virt_core, KProcess* owner, ThreadType type); - [[nodiscard]] static ResultCode InitializeThread(KThread* thread, KThreadFunction func, - uintptr_t arg, VAddr user_stack_top, s32 prio, - s32 core, KProcess* owner, ThreadType type, - std::function<void(void*)>&& init_func, - void* init_func_parameter); + [[nodiscard]] static Result InitializeThread(KThread* thread, KThreadFunction func, + uintptr_t arg, VAddr user_stack_top, s32 prio, + s32 core, KProcess* owner, ThreadType type, + std::function<void()>&& init_func); static void RestorePriority(KernelCore& kernel_ctx, KThread* thread); @@ -741,7 +746,7 @@ private: u32 suspend_request_flags{}; u32 suspend_allowed_flags{}; s32 synced_index{}; - ResultCode wait_result{ResultSuccess}; + Result wait_result{ResultSuccess}; s32 base_priority{}; s32 physical_ideal_core_id{}; s32 virtual_ideal_core_id{}; @@ -751,7 +756,7 @@ private: KAffinityMask original_physical_affinity_mask{}; s32 original_physical_ideal_core_id{}; s32 num_core_migration_disables{}; - ThreadState thread_state{}; + std::atomic<ThreadState> thread_state{}; std::atomic<bool> termination_requested{}; bool wait_cancelled{}; bool cancellable{}; @@ -761,18 +766,22 @@ private: s8 priority_inheritance_count{}; bool resource_limit_release_hint{}; StackParameters stack_parameters{}; - KSpinLock context_guard{}; - KSpinLock dummy_wait_lock{}; + Common::SpinLock context_guard{}; // For emulation std::shared_ptr<Common::Fiber> host_context{}; bool is_single_core{}; ThreadType thread_type{}; + StepState step_state{}; + std::mutex dummy_wait_lock; + std::condition_variable dummy_wait_cv; // For debugging std::vector<KSynchronizationObject*> wait_objects_for_debugging; VAddr mutex_wait_address_for_debugging{}; ThreadWaitReasonForDebugging wait_reason_for_debugging{}; + uintptr_t argument; + VAddr stack_top; public: using ConditionVariableThreadTreeType = ConditionVariableThreadTree; diff --git a/src/core/hle/kernel/k_thread_local_page.cpp b/src/core/hle/kernel/k_thread_local_page.cpp new file mode 100644 index 000000000..563560114 --- /dev/null +++ b/src/core/hle/kernel/k_thread_local_page.cpp @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/scope_exit.h" +#include "core/core.h" + +#include "core/hle/kernel/k_memory_block.h" +#include "core/hle/kernel/k_page_buffer.h" +#include "core/hle/kernel/k_page_table.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_thread_local_page.h" +#include "core/hle/kernel/kernel.h" + +namespace Kernel { + +Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) { + // Set that this process owns us. + m_owner = process; + m_kernel = &kernel; + + // Allocate a new page. + KPageBuffer* page_buf = KPageBuffer::Allocate(kernel); + R_UNLESS(page_buf != nullptr, ResultOutOfMemory); + auto page_buf_guard = SCOPE_GUARD({ KPageBuffer::Free(kernel, page_buf); }); + + // Map the address in. + const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf); + R_TRY(m_owner->PageTable().MapPages(std::addressof(m_virt_addr), 1, PageSize, phys_addr, + KMemoryState::ThreadLocal, + KMemoryPermission::UserReadWrite)); + + // We succeeded. + page_buf_guard.Cancel(); + + return ResultSuccess; +} + +Result KThreadLocalPage::Finalize() { + // Get the physical address of the page. + const PAddr phys_addr = m_owner->PageTable().GetPhysicalAddr(m_virt_addr); + ASSERT(phys_addr); + + // Unmap the page. + R_TRY(m_owner->PageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal)); + + // Free the page. + KPageBuffer::Free(*m_kernel, KPageBuffer::FromPhysicalAddress(m_kernel->System(), phys_addr)); + + return ResultSuccess; +} + +VAddr KThreadLocalPage::Reserve() { + for (size_t i = 0; i < m_is_region_free.size(); i++) { + if (m_is_region_free[i]) { + m_is_region_free[i] = false; + return this->GetRegionAddress(i); + } + } + + return 0; +} + +void KThreadLocalPage::Release(VAddr addr) { + m_is_region_free[this->GetRegionIndex(addr)] = true; +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h new file mode 100644 index 000000000..0a7f22680 --- /dev/null +++ b/src/core/hle/kernel/k_thread_local_page.h @@ -0,0 +1,110 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <algorithm> +#include <array> + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/common_types.h" +#include "common/intrusive_red_black_tree.h" +#include "core/hle/kernel/memory_types.h" +#include "core/hle/kernel/slab_helpers.h" +#include "core/hle/result.h" + +namespace Kernel { + +class KernelCore; +class KProcess; + +class KThreadLocalPage final : public Common::IntrusiveRedBlackTreeBaseNode<KThreadLocalPage>, + public KSlabAllocated<KThreadLocalPage> { +public: + static constexpr size_t RegionsPerPage = PageSize / Svc::ThreadLocalRegionSize; + static_assert(RegionsPerPage > 0); + +public: + constexpr explicit KThreadLocalPage(VAddr addr = {}) : m_virt_addr(addr) { + m_is_region_free.fill(true); + } + + constexpr VAddr GetAddress() const { + return m_virt_addr; + } + + Result Initialize(KernelCore& kernel, KProcess* process); + Result Finalize(); + + VAddr Reserve(); + void Release(VAddr addr); + + bool IsAllUsed() const { + return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(), + [](bool is_free) { return !is_free; }); + } + + bool IsAllFree() const { + return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(), + [](bool is_free) { return is_free; }); + } + + bool IsAnyUsed() const { + return !this->IsAllFree(); + } + + bool IsAnyFree() const { + return !this->IsAllUsed(); + } + +public: + using RedBlackKeyType = VAddr; + + static constexpr RedBlackKeyType GetRedBlackKey(const RedBlackKeyType& v) { + return v; + } + static constexpr RedBlackKeyType GetRedBlackKey(const KThreadLocalPage& v) { + return v.GetAddress(); + } + + template <typename T> + requires(std::same_as<T, KThreadLocalPage> || + std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs, + const KThreadLocalPage& + rhs) { + const VAddr lval = GetRedBlackKey(lhs); + const VAddr rval = GetRedBlackKey(rhs); + + if (lval < rval) { + return -1; + } else if (lval == rval) { + return 0; + } else { + return 1; + } + } + +private: + constexpr VAddr GetRegionAddress(size_t i) const { + return this->GetAddress() + i * Svc::ThreadLocalRegionSize; + } + + constexpr bool Contains(VAddr addr) const { + return this->GetAddress() <= addr && addr < this->GetAddress() + PageSize; + } + + constexpr size_t GetRegionIndex(VAddr addr) const { + ASSERT(Common::IsAligned(addr, Svc::ThreadLocalRegionSize)); + ASSERT(this->Contains(addr)); + return (addr - this->GetAddress()) / Svc::ThreadLocalRegionSize; + } + +private: + VAddr m_virt_addr{}; + KProcess* m_owner{}; + KernelCore* m_kernel{}; + std::array<bool, RegionsPerPage> m_is_region_free{}; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_thread_queue.cpp b/src/core/hle/kernel/k_thread_queue.cpp index d5248b547..9f4e081ba 100644 --- a/src/core/hle/kernel/k_thread_queue.cpp +++ b/src/core/hle/kernel/k_thread_queue.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_thread_queue.h" #include "core/hle/kernel/kernel.h" @@ -10,9 +9,9 @@ namespace Kernel { void KThreadQueue::NotifyAvailable([[maybe_unused]] KThread* waiting_thread, [[maybe_unused]] KSynchronizationObject* signaled_object, - [[maybe_unused]] ResultCode wait_result) {} + [[maybe_unused]] Result wait_result) {} -void KThreadQueue::EndWait(KThread* waiting_thread, ResultCode wait_result) { +void KThreadQueue::EndWait(KThread* waiting_thread, Result wait_result) { // Set the thread's wait result. waiting_thread->SetWaitResult(wait_result); @@ -26,8 +25,7 @@ void KThreadQueue::EndWait(KThread* waiting_thread, ResultCode wait_result) { kernel.TimeManager().UnscheduleTimeEvent(waiting_thread); } -void KThreadQueue::CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task) { +void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) { // Set the thread's wait result. waiting_thread->SetWaitResult(wait_result); @@ -44,6 +42,6 @@ void KThreadQueue::CancelWait(KThread* waiting_thread, ResultCode wait_result, } void KThreadQueueWithoutEndWait::EndWait([[maybe_unused]] KThread* waiting_thread, - [[maybe_unused]] ResultCode wait_result) {} + [[maybe_unused]] Result wait_result) {} } // namespace Kernel diff --git a/src/core/hle/kernel/k_thread_queue.h b/src/core/hle/kernel/k_thread_queue.h index ccb718e49..8d76ece81 100644 --- a/src/core/hle/kernel/k_thread_queue.h +++ b/src/core/hle/kernel/k_thread_queue.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -15,10 +14,9 @@ public: virtual ~KThreadQueue() = default; virtual void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object, - ResultCode wait_result); - virtual void EndWait(KThread* waiting_thread, ResultCode wait_result); - virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task); + Result wait_result); + virtual void EndWait(KThread* waiting_thread, Result wait_result); + virtual void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task); private: KernelCore& kernel; @@ -29,7 +27,7 @@ class KThreadQueueWithoutEndWait : public KThreadQueue { public: explicit KThreadQueueWithoutEndWait(KernelCore& kernel_) : KThreadQueue(kernel_) {} - void EndWait(KThread* waiting_thread, ResultCode wait_result) override final; + void EndWait(KThread* waiting_thread, Result wait_result) override final; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_trace.h b/src/core/hle/kernel/k_trace.h index d3fed1888..d61af4830 100644 --- a/src/core/hle/kernel/k_trace.h +++ b/src/core/hle/kernel/k_trace.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp index 1732925c9..b0320eb73 100644 --- a/src/core/hle/kernel/k_transfer_memory.cpp +++ b/src/core/hle/kernel/k_transfer_memory.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_resource_limit.h" @@ -14,8 +13,8 @@ KTransferMemory::KTransferMemory(KernelCore& kernel_) KTransferMemory::~KTransferMemory() = default; -ResultCode KTransferMemory::Initialize(VAddr address_, std::size_t size_, - Svc::MemoryPermission owner_perm_) { +Result KTransferMemory::Initialize(VAddr address_, std::size_t size_, + Svc::MemoryPermission owner_perm_) { // Set members. owner = kernel.CurrentProcess(); diff --git a/src/core/hle/kernel/k_transfer_memory.h b/src/core/hle/kernel/k_transfer_memory.h index cb7521823..85d508ee7 100644 --- a/src/core/hle/kernel/k_transfer_memory.h +++ b/src/core/hle/kernel/k_transfer_memory.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,7 +7,7 @@ #include "core/hle/kernel/svc_types.h" #include "core/hle/result.h" -union ResultCode; +union Result; namespace Core::Memory { class Memory; @@ -27,7 +26,7 @@ public: explicit KTransferMemory(KernelCore& kernel_); ~KTransferMemory() override; - ResultCode Initialize(VAddr address_, std::size_t size_, Svc::MemoryPermission owner_perm_); + Result Initialize(VAddr address_, std::size_t size_, Svc::MemoryPermission owner_perm_); void Finalize() override; diff --git a/src/core/hle/kernel/k_worker_task.h b/src/core/hle/kernel/k_worker_task.h index b7794c6a8..ef591d831 100644 --- a/src/core/hle/kernel/k_worker_task.h +++ b/src/core/hle/kernel/k_worker_task.h @@ -1,6 +1,5 @@ -// Copyright 2022 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_worker_task_manager.cpp b/src/core/hle/kernel/k_worker_task_manager.cpp index 785e08111..04042bf8f 100644 --- a/src/core/hle/kernel/k_worker_task_manager.cpp +++ b/src/core/hle/kernel/k_worker_task_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2022 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "core/hle/kernel/k_process.h" @@ -24,7 +23,7 @@ void KWorkerTask::DoWorkerTask() { } } -KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "yuzu:KWorkerTaskManager") {} +KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "KWorkerTaskManager") {} void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) { ASSERT(type <= WorkerType::Count); diff --git a/src/core/hle/kernel/k_worker_task_manager.h b/src/core/hle/kernel/k_worker_task_manager.h index 43d1bfcec..f6618883e 100644 --- a/src/core/hle/kernel/k_worker_task_manager.h +++ b/src/core/hle/kernel/k_worker_task_manager.h @@ -1,6 +1,5 @@ -// Copyright 2022 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_writable_event.cpp b/src/core/hle/kernel/k_writable_event.cpp index bdb1db6d5..ff88c5acd 100644 --- a/src/core/hle/kernel/k_writable_event.cpp +++ b/src/core/hle/kernel/k_writable_event.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" @@ -19,11 +18,11 @@ void KWritableEvent::Initialize(KEvent* parent_event_, std::string&& name_) { parent->GetReadableEvent().Open(); } -ResultCode KWritableEvent::Signal() { +Result KWritableEvent::Signal() { return parent->GetReadableEvent().Signal(); } -ResultCode KWritableEvent::Clear() { +Result KWritableEvent::Clear() { return parent->GetReadableEvent().Clear(); } diff --git a/src/core/hle/kernel/k_writable_event.h b/src/core/hle/kernel/k_writable_event.h index 858d982c4..3fd0c7d0a 100644 --- a/src/core/hle/kernel/k_writable_event.h +++ b/src/core/hle/kernel/k_writable_event.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -26,8 +25,8 @@ public: static void PostDestroy([[maybe_unused]] uintptr_t arg) {} void Initialize(KEvent* parent_, std::string&& name_); - ResultCode Signal(); - ResultCode Clear(); + Result Signal(); + Result Clear(); KEvent* GetParent() const { return parent; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 49c0714ed..9251f29ad 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include <atomic> @@ -18,13 +17,10 @@ #include "common/thread.h" #include "common/thread_worker.h" #include "core/arm/arm_interface.h" -#include "core/arm/cpu_interrupt_handler.h" #include "core/arm/exclusive_monitor.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/core_timing_util.h" #include "core/cpu_manager.h" -#include "core/device_memory.h" #include "core/hardware_properties.h" #include "core/hle/kernel/init/init_slab_setup.h" #include "core/hle/kernel/k_client_port.h" @@ -35,7 +31,6 @@ #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_shared_memory.h" -#include "core/hle/kernel/k_slab_heap.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_worker_task_manager.h" #include "core/hle/kernel/kernel.h" @@ -52,42 +47,41 @@ namespace Kernel { struct KernelCore::Impl { explicit Impl(Core::System& system_, KernelCore& kernel_) - : time_manager{system_}, object_list_container{kernel_}, - service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {} + : time_manager{system_}, + service_threads_manager{1, "ServiceThreadsManager"}, system{system_} {} void SetMulticore(bool is_multi) { is_multicore = is_multi; } void Initialize(KernelCore& kernel) { + global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel); global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); global_handle_table->Initialize(KHandleTable::MaxTableSize); + default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread"); is_phantom_mode_for_singlecore = false; - InitializePhysicalCores(); - // Derive the initial memory layout from the emulated board Init::InitializeSlabResourceCounts(kernel); - KMemoryLayout memory_layout; - DeriveInitialMemoryLayout(memory_layout); - Init::InitializeSlabHeaps(system, memory_layout); + DeriveInitialMemoryLayout(); + Init::InitializeSlabHeaps(system, *memory_layout); // Initialize kernel memory and resources. - InitializeSystemResourceLimit(kernel, system.CoreTiming(), memory_layout); - InitializeMemoryLayout(memory_layout); - InitializePageSlab(); - InitializeSchedulers(); - InitializeSuspendThreads(); + InitializeSystemResourceLimit(kernel, system.CoreTiming()); + InitializeMemoryLayout(); + Init::InitializeKPageBufferSlabHeap(system); + InitializeShutdownThreads(); InitializePreemption(kernel); + InitializePhysicalCores(); RegisterHostThread(); } void InitializeCores() { for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { - cores[core_id].Initialize(current_process->Is64BitProcess()); + cores[core_id]->Initialize((*current_process).Is64BitProcess()); system.Memory().SetCurrentPageTable(*current_process, core_id); } } @@ -98,39 +92,16 @@ struct KernelCore::Impl { process_list.clear(); - // Close all open server ports. - std::unordered_set<KServerPort*> server_ports_; - { - std::lock_guard lk(server_ports_lock); - server_ports_ = server_ports; - server_ports.clear(); - } - for (auto* server_port : server_ports_) { - server_port->Close(); - } - // Close all open server sessions. - std::unordered_set<KServerSession*> server_sessions_; - { - std::lock_guard lk(server_sessions_lock); - server_sessions_ = server_sessions; - server_sessions.clear(); - } - for (auto* server_session : server_sessions_) { - server_session->Close(); - } - - // Ensure that the object list container is finalized and properly shutdown. - object_list_container.Finalize(); - - // Ensures all service threads gracefully shutdown. - ClearServiceThreads(); + CloseServices(); next_object_id = 0; next_kernel_process_id = KProcess::InitialKIPIDMin; next_user_process_id = KProcess::ProcessIDMin; next_thread_id = 1; - cores.clear(); + for (auto& core : cores) { + core = nullptr; + } global_handle_table->Finalize(); global_handle_table.reset(); @@ -155,15 +126,15 @@ struct KernelCore::Impl { CleanupObject(font_shared_mem); CleanupObject(irs_shared_mem); CleanupObject(time_shared_mem); + CleanupObject(hidbus_shared_mem); CleanupObject(system_resource_limit); for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { - if (suspend_threads[core_id]) { - suspend_threads[core_id]->Close(); - suspend_threads[core_id] = nullptr; + if (shutdown_threads[core_id]) { + shutdown_threads[core_id]->Close(); + shutdown_threads[core_id] = nullptr; } - schedulers[core_id]->Finalize(); schedulers[core_id].reset(); } @@ -172,7 +143,7 @@ struct KernelCore::Impl { // Close kernel objects that were not freed on shutdown { - std::lock_guard lk(registered_in_use_objects_lock); + std::scoped_lock lk{registered_in_use_objects_lock}; if (registered_in_use_objects.size()) { for (auto& object : registered_in_use_objects) { object->Close(); @@ -183,48 +154,76 @@ struct KernelCore::Impl { // Shutdown all processes. if (current_process) { - current_process->Finalize(); + (*current_process).Finalize(); // current_process->Close(); // TODO: The current process should be destroyed based on accurate ref counting after // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak. - current_process->Destroy(); + (*current_process).Destroy(); current_process = nullptr; } // Track kernel objects that were not freed on shutdown { - std::lock_guard lk(registered_objects_lock); + std::scoped_lock lk{registered_objects_lock}; if (registered_objects.size()) { - LOG_WARNING(Kernel, "{} kernel objects were dangling on shutdown!", - registered_objects.size()); + LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!", + registered_objects.size()); registered_objects.clear(); } } + + // Ensure that the object list container is finalized and properly shutdown. + global_object_list_container->Finalize(); + global_object_list_container.reset(); + } + + void CloseServices() { + // Close all open server sessions and ports. + std::unordered_set<KAutoObject*> server_objects_; + { + std::scoped_lock lk(server_objects_lock); + server_objects_ = server_objects; + server_objects.clear(); + } + for (auto* server_object : server_objects_) { + server_object->Close(); + } + + // Ensures all service threads gracefully shutdown. + ClearServiceThreads(); } void InitializePhysicalCores() { exclusive_monitor = Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES); for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { - schedulers[i] = std::make_unique<Kernel::KScheduler>(system, i); - cores.emplace_back(i, system, *schedulers[i], interrupts); - } - } + const s32 core{static_cast<s32>(i)}; - void InitializeSchedulers() { - for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { - cores[i].Scheduler().Initialize(); + schedulers[i] = std::make_unique<Kernel::KScheduler>(system.Kernel()); + cores[i] = std::make_unique<Kernel::PhysicalCore>(i, system, *schedulers[i]); + + auto* main_thread{Kernel::KThread::Create(system.Kernel())}; + main_thread->SetName(fmt::format("MainThread:{}", core)); + main_thread->SetCurrentCore(core); + ASSERT(Kernel::KThread::InitializeMainThread(system, main_thread, core).IsSuccess()); + + auto* idle_thread{Kernel::KThread::Create(system.Kernel())}; + idle_thread->SetCurrentCore(core); + ASSERT(Kernel::KThread::InitializeIdleThread(system, idle_thread, core).IsSuccess()); + + schedulers[i]->Initialize(main_thread, idle_thread, core); } } // Creates the default system resource limit void InitializeSystemResourceLimit(KernelCore& kernel, - const Core::Timing::CoreTiming& core_timing, - const KMemoryLayout& memory_layout) { + const Core::Timing::CoreTiming& core_timing) { system_resource_limit = KResourceLimit::Create(system.Kernel()); system_resource_limit->Initialize(&core_timing); - const auto [total_size, kernel_size] = memory_layout.GetTotalAndKernelMemorySizes(); + const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()}; + const auto total_size{sizes.first}; + const auto kernel_size{sizes.second}; // If setting the default system values fails, then something seriously wrong has occurred. ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size) @@ -240,37 +239,31 @@ struct KernelCore::Impl { constexpr u64 secure_applet_memory_size{4_MiB}; ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, secure_applet_memory_size)); - - // This memory seems to be reserved on hardware, but is not reserved/used by yuzu. - // Likely Horizon OS reserved memory - // TODO(ameerj): Derive the memory rather than hardcode it. - constexpr u64 unknown_reserved_memory{0x2f896000}; - ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, - unknown_reserved_memory)); } void InitializePreemption(KernelCore& kernel) { preemption_event = Core::Timing::CreateEvent( - "PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) { + "PreemptionCallback", + [this, &kernel](std::uintptr_t, s64 time, + std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { { KScopedSchedulerLock lock(kernel); global_scheduler_context->PreemptThreads(); } - const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; - system.CoreTiming().ScheduleEvent(time_interval, preemption_event); + return std::nullopt; }); const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; - system.CoreTiming().ScheduleEvent(time_interval, preemption_event); + system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event); } - void InitializeSuspendThreads() { + void InitializeShutdownThreads() { for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { - suspend_threads[core_id] = KThread::Create(system.Kernel()); - ASSERT(KThread::InitializeHighPriorityThread(system, suspend_threads[core_id], {}, {}, + shutdown_threads[core_id] = KThread::Create(system.Kernel()); + ASSERT(KThread::InitializeHighPriorityThread(system, shutdown_threads[core_id], {}, {}, core_id) .IsSuccess()); - suspend_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id)); + shutdown_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id)); } } @@ -300,15 +293,16 @@ struct KernelCore::Impl { // Gets the dummy KThread for the caller, allocating a new one if this is the first time KThread* GetHostDummyThread() { - auto make_thread = [this]() { - KThread* thread = KThread::Create(system.Kernel()); + auto initialize = [this](KThread* thread) { ASSERT(KThread::InitializeDummyThread(thread).IsSuccess()); thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); return thread; }; - thread_local KThread* saved_thread = make_thread(); - return saved_thread; + thread_local auto raw_thread = KThread(system.Kernel()); + thread_local auto thread = initialize(&raw_thread); + + return thread; } /// Registers a CPU core thread by allocating a host thread ID for it @@ -347,6 +341,8 @@ struct KernelCore::Impl { return is_shutting_down.load(std::memory_order_relaxed); } + static inline thread_local KThread* current_thread{nullptr}; + KThread* GetCurrentEmuThread() { // If we are shutting down the kernel, none of this is relevant anymore. if (IsShuttingDown()) { @@ -357,19 +353,26 @@ struct KernelCore::Impl { if (thread_id >= Core::Hardware::NUM_CPU_CORES) { return GetHostDummyThread(); } - return schedulers[thread_id]->GetCurrentThread(); + + return current_thread; + } + + void SetCurrentEmuThread(KThread* thread) { + current_thread = thread; } - void DeriveInitialMemoryLayout(KMemoryLayout& memory_layout) { + void DeriveInitialMemoryLayout() { + memory_layout = std::make_unique<KMemoryLayout>(); + // Insert the root region for the virtual memory tree, from which all other regions will // derive. - memory_layout.GetVirtualMemoryRegionTree().InsertDirectly( + memory_layout->GetVirtualMemoryRegionTree().InsertDirectly( KernelVirtualAddressSpaceBase, KernelVirtualAddressSpaceBase + KernelVirtualAddressSpaceSize - 1); // Insert the root region for the physical memory tree, from which all other regions will // derive. - memory_layout.GetPhysicalMemoryRegionTree().InsertDirectly( + memory_layout->GetPhysicalMemoryRegionTree().InsertDirectly( KernelPhysicalAddressSpaceBase, KernelPhysicalAddressSpaceBase + KernelPhysicalAddressSpaceSize - 1); @@ -386,7 +389,7 @@ struct KernelCore::Impl { if (!(kernel_region_start + KernelRegionSize - 1 <= KernelVirtualAddressSpaceLast)) { kernel_region_size = KernelVirtualAddressSpaceEnd - kernel_region_start; } - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( kernel_region_start, kernel_region_size, KMemoryRegionType_Kernel)); // Setup the code region. @@ -395,11 +398,11 @@ struct KernelCore::Impl { Common::AlignDown(code_start_virt_addr, CodeRegionAlign); constexpr VAddr code_region_end = Common::AlignUp(code_end_virt_addr, CodeRegionAlign); constexpr size_t code_region_size = code_region_end - code_region_start; - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( code_region_start, code_region_size, KMemoryRegionType_KernelCode)); // Setup board-specific device physical regions. - Init::SetupDevicePhysicalMemoryRegions(memory_layout); + Init::SetupDevicePhysicalMemoryRegions(*memory_layout); // Determine the amount of space needed for the misc region. size_t misc_region_needed_size; @@ -408,7 +411,7 @@ struct KernelCore::Impl { misc_region_needed_size = Core::Hardware::NUM_CPU_CORES * (3 * (PageSize + PageSize)); // Account for each auto-map device. - for (const auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { + for (const auto& region : memory_layout->GetPhysicalMemoryRegionTree()) { if (region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) { // Check that the region is valid. ASSERT(region.GetEndAddress() != 0); @@ -433,22 +436,22 @@ struct KernelCore::Impl { // Setup the misc region. const VAddr misc_region_start = - memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( + memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion( misc_region_size, MiscRegionAlign, KMemoryRegionType_Kernel); - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc)); // Setup the stack region. constexpr size_t StackRegionSize = 14_MiB; constexpr size_t StackRegionAlign = KernelAslrAlignment; const VAddr stack_region_start = - memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( + memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion( StackRegionSize, StackRegionAlign, KMemoryRegionType_Kernel); - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( stack_region_start, StackRegionSize, KMemoryRegionType_KernelStack)); // Determine the size of the resource region. - const size_t resource_region_size = memory_layout.GetResourceRegionSizeForInit(); + const size_t resource_region_size = memory_layout->GetResourceRegionSizeForInit(); // Determine the size of the slab region. const size_t slab_region_size = @@ -465,23 +468,23 @@ struct KernelCore::Impl { Common::AlignUp(code_end_phys_addr + slab_region_size, SlabRegionAlign) - Common::AlignDown(code_end_phys_addr, SlabRegionAlign); const VAddr slab_region_start = - memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( + memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion( slab_region_needed_size, SlabRegionAlign, KMemoryRegionType_Kernel) + (code_end_phys_addr % SlabRegionAlign); - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( slab_region_start, slab_region_size, KMemoryRegionType_KernelSlab)); // Setup the temp region. constexpr size_t TempRegionSize = 128_MiB; constexpr size_t TempRegionAlign = KernelAslrAlignment; const VAddr temp_region_start = - memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( + memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion( TempRegionSize, TempRegionAlign, KMemoryRegionType_Kernel); - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(temp_region_start, TempRegionSize, - KMemoryRegionType_KernelTemp)); + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(temp_region_start, TempRegionSize, + KMemoryRegionType_KernelTemp)); // Automatically map in devices that have auto-map attributes. - for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { + for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) { // We only care about kernel regions. if (!region.IsDerivedFrom(KMemoryRegionType_Kernel)) { continue; @@ -508,21 +511,21 @@ struct KernelCore::Impl { const size_t map_size = Common::AlignUp(region.GetEndAddress(), PageSize) - map_phys_addr; const VAddr map_virt_addr = - memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard( + memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard( map_size, PageSize, KMemoryRegionType_KernelMisc, PageSize); - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( map_virt_addr, map_size, KMemoryRegionType_KernelMiscMappedDevice)); region.SetPairAddress(map_virt_addr + region.GetAddress() - map_phys_addr); } - Init::SetupDramPhysicalMemoryRegions(memory_layout); + Init::SetupDramPhysicalMemoryRegions(*memory_layout); // Insert a physical region for the kernel code region. - ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert( code_start_phys_addr, code_region_size, KMemoryRegionType_DramKernelCode)); // Insert a physical region for the kernel slab region. - ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert( slab_start_phys_addr, slab_region_size, KMemoryRegionType_DramKernelSlab)); // Determine size available for kernel page table heaps, requiring > 8 MB. @@ -531,12 +534,12 @@ struct KernelCore::Impl { ASSERT(page_table_heap_size / 4_MiB > 2); // Insert a physical region for the kernel page table heap region - ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert( slab_end_phys_addr, page_table_heap_size, KMemoryRegionType_DramKernelPtHeap)); // All DRAM regions that we haven't tagged by this point will be mapped under the linear // mapping. Tag them. - for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { + for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) { if (region.GetType() == KMemoryRegionType_Dram) { // Check that the region is valid. ASSERT(region.GetEndAddress() != 0); @@ -548,7 +551,7 @@ struct KernelCore::Impl { // Get the linear region extents. const auto linear_extents = - memory_layout.GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + memory_layout->GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( KMemoryRegionAttr_LinearMapped); ASSERT(linear_extents.GetEndAddress() != 0); @@ -560,7 +563,7 @@ struct KernelCore::Impl { Common::AlignUp(linear_extents.GetEndAddress(), LinearRegionAlign) - aligned_linear_phys_start; const VAddr linear_region_start = - memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard( + memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard( linear_region_size, LinearRegionAlign, KMemoryRegionType_None, LinearRegionAlign); const u64 linear_region_phys_to_virt_diff = linear_region_start - aligned_linear_phys_start; @@ -569,7 +572,7 @@ struct KernelCore::Impl { { PAddr cur_phys_addr = 0; u64 cur_size = 0; - for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { + for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) { if (!region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) { continue; } @@ -588,55 +591,49 @@ struct KernelCore::Impl { const VAddr region_virt_addr = region.GetAddress() + linear_region_phys_to_virt_diff; - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( region_virt_addr, region.GetSize(), GetTypeForVirtualLinearMapping(region.GetType()))); region.SetPairAddress(region_virt_addr); KMemoryRegion* virt_region = - memory_layout.GetVirtualMemoryRegionTree().FindModifiable(region_virt_addr); + memory_layout->GetVirtualMemoryRegionTree().FindModifiable(region_virt_addr); ASSERT(virt_region != nullptr); virt_region->SetPairAddress(region.GetAddress()); } } // Insert regions for the initial page table region. - ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert( resource_end_phys_addr, KernelPageTableHeapSize, KMemoryRegionType_DramKernelInitPt)); - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( resource_end_phys_addr + linear_region_phys_to_virt_diff, KernelPageTableHeapSize, KMemoryRegionType_VirtualDramKernelInitPt)); // All linear-mapped DRAM regions that we haven't tagged by this point will be allocated to // some pool partition. Tag them. - for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { + for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) { if (region.GetType() == (KMemoryRegionType_Dram | KMemoryRegionAttr_LinearMapped)) { region.SetType(KMemoryRegionType_DramPoolPartition); } } // Setup all other memory regions needed to arrange the pool partitions. - Init::SetupPoolPartitionMemoryRegions(memory_layout); + Init::SetupPoolPartitionMemoryRegions(*memory_layout); // Cache all linear regions in their own trees for faster access, later. - memory_layout.InitializeLinearMemoryRegionTrees(aligned_linear_phys_start, - linear_region_start); + memory_layout->InitializeLinearMemoryRegionTrees(aligned_linear_phys_start, + linear_region_start); } - void InitializeMemoryLayout(const KMemoryLayout& memory_layout) { - const auto system_pool = memory_layout.GetKernelSystemPoolRegionPhysicalExtents(); - const auto applet_pool = memory_layout.GetKernelAppletPoolRegionPhysicalExtents(); - const auto application_pool = memory_layout.GetKernelApplicationPoolRegionPhysicalExtents(); + void InitializeMemoryLayout() { + const auto system_pool = memory_layout->GetKernelSystemPoolRegionPhysicalExtents(); - // Initialize memory managers + // Initialize the memory manager. memory_manager = std::make_unique<KMemoryManager>(system); - memory_manager->InitializeManager(KMemoryManager::Pool::Application, - application_pool.GetAddress(), - application_pool.GetEndAddress()); - memory_manager->InitializeManager(KMemoryManager::Pool::Applet, applet_pool.GetAddress(), - applet_pool.GetEndAddress()); - memory_manager->InitializeManager(KMemoryManager::Pool::System, system_pool.GetAddress(), - system_pool.GetEndAddress()); + const auto& management_region = memory_layout->GetPoolManagementRegion(); + ASSERT(management_region.GetEndAddress() != 0); + memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize()); // Setup memory regions for emulated processes // TODO(bunnei): These should not be hardcoded regions initialized within the kernel @@ -644,16 +641,20 @@ struct KernelCore::Impl { constexpr std::size_t font_size{0x1100000}; constexpr std::size_t irs_size{0x8000}; constexpr std::size_t time_size{0x1000}; + constexpr std::size_t hidbus_size{0x1000}; const PAddr hid_phys_addr{system_pool.GetAddress()}; const PAddr font_phys_addr{system_pool.GetAddress() + hid_size}; const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size}; const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size}; + const PAddr hidbus_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size + + time_size}; hid_shared_mem = KSharedMemory::Create(system.Kernel()); font_shared_mem = KSharedMemory::Create(system.Kernel()); irs_shared_mem = KSharedMemory::Create(system.Kernel()); time_shared_mem = KSharedMemory::Create(system.Kernel()); + hidbus_shared_mem = KSharedMemory::Create(system.Kernel()); hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, {hid_phys_addr, hid_size / PageSize}, @@ -671,22 +672,10 @@ struct KernelCore::Impl { {time_phys_addr, time_size / PageSize}, Svc::MemoryPermission::None, Svc::MemoryPermission::Read, time_phys_addr, time_size, "Time:SharedMemory"); - } - - void InitializePageSlab() { - // Allocate slab heaps - user_slab_heap_pages = - std::make_unique<KSlabHeap<Page>>(KSlabHeap<Page>::AllocationType::Guest); - - // TODO(ameerj): This should be derived, not hardcoded within the kernel - constexpr u64 user_slab_heap_size{0x3de000}; - // Reserve slab heaps - ASSERT( - system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size)); - // Initialize slab heap - user_slab_heap_pages->Initialize( - system.DeviceMemory().GetPointer(Core::DramMemoryMap::SlabHeapBase), - user_slab_heap_size); + hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr, + {hidbus_phys_addr, hidbus_size / PageSize}, + Svc::MemoryPermission::None, Svc::MemoryPermission::Read, + hidbus_phys_addr, hidbus_size, "HidBus:SharedMemory"); } KClientPort* CreateNamedServicePort(std::string name) { @@ -697,13 +686,20 @@ struct KernelCore::Impl { } KClientPort* port = &search->second(system.ServiceManager(), system); - { - std::lock_guard lk(server_ports_lock); - server_ports.insert(&port->GetParent()->GetServerPort()); - } + RegisterServerObject(&port->GetParent()->GetServerPort()); return port; } + void RegisterServerObject(KAutoObject* server_object) { + std::scoped_lock lk(server_objects_lock); + server_objects.insert(server_object); + } + + void UnregisterServerObject(KAutoObject* server_object) { + std::scoped_lock lk(server_objects_lock); + server_objects.erase(server_object); + } + std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel, const std::string& name) { auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name); @@ -716,6 +712,12 @@ struct KernelCore::Impl { void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { if (auto strong_ptr = service_thread.lock()) { + if (strong_ptr == default_service_thread.lock()) { + // Nothing to do here, the service is using default_service_thread, which will be + // released on shutdown. + return; + } + service_threads_manager.QueueWork( [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); }); } @@ -725,8 +727,7 @@ struct KernelCore::Impl { service_threads_manager.QueueWork([this]() { service_threads.clear(); }); } - std::mutex server_ports_lock; - std::mutex server_sessions_lock; + std::mutex server_objects_lock; std::mutex registered_objects_lock; std::mutex registered_in_use_objects_lock; @@ -737,7 +738,7 @@ struct KernelCore::Impl { // Lists all processes that exist in the current session. std::vector<KProcess*> process_list; - KProcess* current_process{}; + std::atomic<KProcess*> current_process{}; std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; Kernel::TimeManager time_manager; @@ -750,39 +751,41 @@ struct KernelCore::Impl { // stores all the objects in place. std::unique_ptr<KHandleTable> global_handle_table; - KAutoObjectWithListContainer object_list_container; + std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container; /// Map of named ports managed by the kernel, which can be retrieved using /// the ConnectToPort SVC. std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; NamedPortTable named_ports; - std::unordered_set<KServerPort*> server_ports; - std::unordered_set<KServerSession*> server_sessions; + std::unordered_set<KAutoObject*> server_objects; std::unordered_set<KAutoObject*> registered_objects; std::unordered_set<KAutoObject*> registered_in_use_objects; std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; - std::vector<Kernel::PhysicalCore> cores; + std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores; // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES}; // Kernel memory management std::unique_ptr<KMemoryManager> memory_manager; - std::unique_ptr<KSlabHeap<Page>> user_slab_heap_pages; // Shared memory for services Kernel::KSharedMemory* hid_shared_mem{}; Kernel::KSharedMemory* font_shared_mem{}; Kernel::KSharedMemory* irs_shared_mem{}; Kernel::KSharedMemory* time_shared_mem{}; + Kernel::KSharedMemory* hidbus_shared_mem{}; + + // Memory layout + std::unique_ptr<KMemoryLayout> memory_layout; // Threads used for services - std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads; + std::unordered_set<std::shared_ptr<ServiceThread>> service_threads; + std::weak_ptr<ServiceThread> default_service_thread; Common::ThreadWorker service_threads_manager; - std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads; - std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; + std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads; std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; bool is_multicore{}; @@ -818,6 +821,10 @@ void KernelCore::Shutdown() { impl->Shutdown(); } +void KernelCore::CloseServices() { + impl->CloseServices(); +} + const KResourceLimit* KernelCore::GetSystemResourceLimit() const { return impl->system_resource_limit; } @@ -867,11 +874,11 @@ const Kernel::KScheduler& KernelCore::Scheduler(std::size_t id) const { } Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { - return impl->cores[id]; + return *impl->cores[id]; } const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { - return impl->cores[id]; + return *impl->cores[id]; } size_t KernelCore::CurrentPhysicalCoreIndex() const { @@ -883,11 +890,11 @@ size_t KernelCore::CurrentPhysicalCoreIndex() const { } Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { - return impl->cores[CurrentPhysicalCoreIndex()]; + return *impl->cores[CurrentPhysicalCoreIndex()]; } const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { - return impl->cores[CurrentPhysicalCoreIndex()]; + return *impl->cores[CurrentPhysicalCoreIndex()]; } Kernel::KScheduler* KernelCore::CurrentScheduler() { @@ -899,15 +906,6 @@ Kernel::KScheduler* KernelCore::CurrentScheduler() { return impl->schedulers[core_id].get(); } -std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() { - return impl->interrupts; -} - -const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() - const { - return impl->interrupts; -} - Kernel::TimeManager& KernelCore::TimeManager() { return impl->time_manager; } @@ -925,25 +923,25 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const { } KAutoObjectWithListContainer& KernelCore::ObjectListContainer() { - return impl->object_list_container; + return *impl->global_object_list_container; } const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const { - return impl->object_list_container; + return *impl->global_object_list_container; } void KernelCore::InvalidateAllInstructionCaches() { for (auto& physical_core : impl->cores) { - physical_core.ArmInterface().ClearInstructionCache(); + physical_core->ArmInterface().ClearInstructionCache(); } } void KernelCore::InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size) { for (auto& physical_core : impl->cores) { - if (!physical_core.IsInitialized()) { + if (!physical_core->IsInitialized()) { continue; } - physical_core.ArmInterface().InvalidateCacheRange(addr, size); + physical_core->ArmInterface().InvalidateCacheRange(addr, size); } } @@ -959,33 +957,31 @@ KClientPort* KernelCore::CreateNamedServicePort(std::string name) { return impl->CreateNamedServicePort(std::move(name)); } -void KernelCore::RegisterServerSession(KServerSession* server_session) { - std::lock_guard lk(impl->server_sessions_lock); - impl->server_sessions.insert(server_session); +void KernelCore::RegisterServerObject(KAutoObject* server_object) { + impl->RegisterServerObject(server_object); } -void KernelCore::UnregisterServerSession(KServerSession* server_session) { - std::lock_guard lk(impl->server_sessions_lock); - impl->server_sessions.erase(server_session); +void KernelCore::UnregisterServerObject(KAutoObject* server_object) { + impl->UnregisterServerObject(server_object); } void KernelCore::RegisterKernelObject(KAutoObject* object) { - std::lock_guard lk(impl->registered_objects_lock); + std::scoped_lock lk{impl->registered_objects_lock}; impl->registered_objects.insert(object); } void KernelCore::UnregisterKernelObject(KAutoObject* object) { - std::lock_guard lk(impl->registered_objects_lock); + std::scoped_lock lk{impl->registered_objects_lock}; impl->registered_objects.erase(object); } void KernelCore::RegisterInUseObject(KAutoObject* object) { - std::lock_guard lk(impl->registered_in_use_objects_lock); + std::scoped_lock lk{impl->registered_in_use_objects_lock}; impl->registered_in_use_objects.insert(object); } void KernelCore::UnregisterInUseObject(KAutoObject* object) { - std::lock_guard lk(impl->registered_in_use_objects_lock); + std::scoped_lock lk{impl->registered_in_use_objects_lock}; impl->registered_in_use_objects.erase(object); } @@ -1033,6 +1029,10 @@ KThread* KernelCore::GetCurrentEmuThread() const { return impl->GetCurrentEmuThread(); } +void KernelCore::SetCurrentEmuThread(KThread* thread) { + impl->SetCurrentEmuThread(thread); +} + KMemoryManager& KernelCore::MemoryManager() { return *impl->memory_manager; } @@ -1041,14 +1041,6 @@ const KMemoryManager& KernelCore::MemoryManager() const { return *impl->memory_manager; } -KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() { - return *impl->user_slab_heap_pages; -} - -const KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() const { - return *impl->user_slab_heap_pages; -} - Kernel::KSharedMemory& KernelCore::GetHidSharedMem() { return *impl->hid_shared_mem; } @@ -1081,22 +1073,38 @@ const Kernel::KSharedMemory& KernelCore::GetTimeSharedMem() const { return *impl->time_shared_mem; } -void KernelCore::Suspend(bool in_suspention) { - const bool should_suspend = exception_exited || in_suspention; - { - KScopedSchedulerLock lock(*this); - const auto state = should_suspend ? ThreadState::Runnable : ThreadState::Waiting; - for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { - impl->suspend_threads[core_id]->SetState(state); - impl->suspend_threads[core_id]->SetWaitReasonForDebugging( - ThreadWaitReasonForDebugging::Suspended); - if (!should_suspend) { - impl->suspend_threads[core_id]->DisableDispatch(); +Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() { + return *impl->hidbus_shared_mem; +} + +const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const { + return *impl->hidbus_shared_mem; +} + +void KernelCore::Suspend(bool suspended) { + const bool should_suspend{exception_exited || suspended}; + const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable; + + for (auto* process : GetProcessList()) { + process->SetActivity(activity); + + if (should_suspend) { + // Wait for execution to stop + for (auto* thread : process->GetThreadList()) { + thread->WaitUntilSuspended(); } } } } +void KernelCore::ShutdownCores() { + KScopedSchedulerLock lk{*this}; + + for (auto* thread : impl->shutdown_threads) { + void(thread->Run()); + } +} + bool KernelCore::IsMulticore() const { return impl->is_multicore; } @@ -1122,6 +1130,10 @@ std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std:: return impl->CreateServiceThread(*this, name); } +std::weak_ptr<Kernel::ServiceThread> KernelCore::GetDefaultServiceThread() const { + return impl->default_service_thread; +} + void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { impl->ReleaseServiceThread(service_thread); } @@ -1142,6 +1154,10 @@ const KWorkerTaskManager& KernelCore::WorkerTaskManager() const { return impl->worker_task_manager; } +const KMemoryLayout& KernelCore::MemoryLayout() const { + return *impl->memory_layout; +} + bool KernelCore::IsPhantomModeForSingleCore() const { return impl->IsPhantomModeForSingleCore(); } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 0e04fc3bb..bcf016a97 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,15 +9,12 @@ #include <string> #include <unordered_map> #include <vector> -#include "core/arm/cpu_interrupt_handler.h" #include "core/hardware_properties.h" #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_slab_heap.h" -#include "core/hle/kernel/memory_types.h" #include "core/hle/kernel/svc_common.h" namespace Core { -class CPUInterruptHandler; class ExclusiveMonitor; class System; } // namespace Core @@ -41,7 +37,9 @@ class KClientSession; class KEvent; class KHandleTable; class KLinkedListNode; +class KMemoryLayout; class KMemoryManager; +class KPageBuffer; class KPort; class KProcess; class KResourceLimit; @@ -51,6 +49,7 @@ class KSession; class KSharedMemory; class KSharedMemoryInfo; class KThread; +class KThreadLocalPage; class KTransferMemory; class KWorkerTaskManager; class KWritableEvent; @@ -108,6 +107,9 @@ public: /// Clears all resources in use by the kernel instance. void Shutdown(); + /// Close all active services in use by the kernel instance. + void CloseServices(); + /// Retrieves a shared pointer to the system resource limit instance. const KResourceLimit* GetSystemResourceLimit() const; @@ -179,10 +181,6 @@ public: const KAutoObjectWithListContainer& ObjectListContainer() const; - std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts(); - - const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts() const; - void InvalidateAllInstructionCaches(); void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size); @@ -193,13 +191,13 @@ public: /// Opens a port to a service previously registered with RegisterNamedService. KClientPort* CreateNamedServicePort(std::string name); - /// Registers a server session with the gobal emulation state, to be freed on shutdown. This is - /// necessary because we do not emulate processes for HLE sessions. - void RegisterServerSession(KServerSession* server_session); + /// Registers a server session or port with the gobal emulation state, to be freed on shutdown. + /// This is necessary because we do not emulate processes for HLE sessions and ports. + void RegisterServerObject(KAutoObject* server_object); - /// Unregisters a server session previously registered with RegisterServerSession when it was - /// destroyed during the current emulation session. - void UnregisterServerSession(KServerSession* server_session); + /// Unregisters a server session or port previously registered with RegisterServerSession when + /// it was destroyed during the current emulation session. + void UnregisterServerObject(KAutoObject* server_object); /// Registers all kernel objects with the global emulation state, this is purely for tracking /// leaks after emulation has been shutdown. @@ -223,6 +221,9 @@ public: /// Gets the current host_thread/guest_thread pointer. KThread* GetCurrentEmuThread() const; + /// Sets the current guest_thread pointer. + void SetCurrentEmuThread(KThread* thread); + /// Gets the current host_thread handle. u32 GetCurrentHostThreadID() const; @@ -238,12 +239,6 @@ public: /// Gets the virtual memory manager for the kernel. const KMemoryManager& MemoryManager() const; - /// Gets the slab heap allocated for user space pages. - KSlabHeap<Page>& GetUserSlabHeapPages(); - - /// Gets the slab heap allocated for user space pages. - const KSlabHeap<Page>& GetUserSlabHeapPages() const; - /// Gets the shared memory object for HID services. Kernel::KSharedMemory& GetHidSharedMem(); @@ -268,12 +263,21 @@ public: /// Gets the shared memory object for Time services. const Kernel::KSharedMemory& GetTimeSharedMem() const; - /// Suspend/unsuspend the OS. - void Suspend(bool in_suspention); + /// Gets the shared memory object for HIDBus services. + Kernel::KSharedMemory& GetHidBusSharedMem(); - /// Exceptional exit the OS. + /// Gets the shared memory object for HIDBus services. + const Kernel::KSharedMemory& GetHidBusSharedMem() const; + + /// Suspend/unsuspend all processes. + void Suspend(bool suspend); + + /// Exceptional exit all processes. void ExceptionalExit(); + /// Notify emulated CPU cores to shut down. + void ShutdownCores(); + bool IsMulticore() const; bool IsShuttingDown() const; @@ -283,9 +287,11 @@ public: void ExitSVCProfile(); /** - * Creates an HLE service thread, which are used to execute service routines asynchronously. - * While these are allocated per ServerSession, these need to be owned and managed outside - * of ServerSession to avoid a circular dependency. + * Creates a host thread to execute HLE service requests, which are used to execute service + * routines asynchronously. While these are allocated per ServerSession, these need to be owned + * and managed outside of ServerSession to avoid a circular dependency. In general, most + * services can just use the default service thread, and not need their own host service thread. + * See GetDefaultServiceThread. * @param name String name for the ServerSession creating this thread, used for debug * purposes. * @returns The a weak pointer newly created service thread. @@ -293,6 +299,14 @@ public: std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name); /** + * Gets the default host service thread, which executes HLE service requests. Unless service + * requests need to block on the host, the default service thread should be used in favor of + * creating a new service thread. + * @returns The a weak pointer for the default service thread. + */ + std::weak_ptr<Kernel::ServiceThread> GetDefaultServiceThread() const; + + /** * Releases a HLE service thread, instructing KernelCore to free it. This should be called when * the ServerSession associated with the thread is destroyed. * @param service_thread Service thread to release. @@ -335,6 +349,10 @@ public: return slab_heap_container->writeable_event; } else if constexpr (std::is_same_v<T, KCodeMemory>) { return slab_heap_container->code_memory; + } else if constexpr (std::is_same_v<T, KPageBuffer>) { + return slab_heap_container->page_buffer; + } else if constexpr (std::is_same_v<T, KThreadLocalPage>) { + return slab_heap_container->thread_local_page; } } @@ -350,6 +368,9 @@ public: /// Gets the current worker task manager, used for dispatching KThread/KProcess tasks. const KWorkerTaskManager& WorkerTaskManager() const; + /// Gets the memory layout. + const KMemoryLayout& MemoryLayout() const; + private: friend class KProcess; friend class KThread; @@ -393,6 +414,8 @@ private: KSlabHeap<KTransferMemory> transfer_memory; KSlabHeap<KWritableEvent> writeable_event; KSlabHeap<KCodeMemory> code_memory; + KSlabHeap<KPageBuffer> page_buffer; + KSlabHeap<KThreadLocalPage> thread_local_page; }; std::unique_ptr<SlabHeapContainer> slab_heap_container; diff --git a/src/core/hle/kernel/memory_types.h b/src/core/hle/kernel/memory_types.h index d458f0eca..3975507bd 100644 --- a/src/core/hle/kernel/memory_types.h +++ b/src/core/hle/kernel/memory_types.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index 7477668e4..d4375962f 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp @@ -1,9 +1,6 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later -#include "common/spin_lock.h" -#include "core/arm/cpu_interrupt_handler.h" #include "core/arm/dynarmic/arm_dynarmic_32.h" #include "core/arm/dynarmic/arm_dynarmic_64.h" #include "core/core.h" @@ -13,16 +10,14 @@ namespace Kernel { -PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_, - Core::CPUInterrupts& interrupts_) - : core_index{core_index_}, system{system_}, scheduler{scheduler_}, - interrupts{interrupts_}, guard{std::make_unique<Common::SpinLock>()} { +PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_) + : core_index{core_index_}, system{system_}, scheduler{scheduler_} { #ifdef ARCHITECTURE_x86_64 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. auto& kernel = system.Kernel(); arm_interface = std::make_unique<Core::ARM_Dynarmic_64>( - system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); + system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); #else #error Platform not supported yet. #endif @@ -36,7 +31,7 @@ void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) { if (!is_64_bit) { // We already initialized a 64-bit core, replace with a 32-bit one. arm_interface = std::make_unique<Core::ARM_Dynarmic_32>( - system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); + system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); } #else #error Platform not supported yet. @@ -45,26 +40,30 @@ void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) { void PhysicalCore::Run() { arm_interface->Run(); + arm_interface->ClearExclusiveState(); } void PhysicalCore::Idle() { - interrupts[core_index].AwaitInterrupt(); + std::unique_lock lk{guard}; + on_interrupt.wait(lk, [this] { return is_interrupted; }); } bool PhysicalCore::IsInterrupted() const { - return interrupts[core_index].IsInterrupted(); + return is_interrupted; } void PhysicalCore::Interrupt() { - guard->lock(); - interrupts[core_index].SetInterrupt(true); - guard->unlock(); + std::unique_lock lk{guard}; + is_interrupted = true; + arm_interface->SignalInterrupt(); + on_interrupt.notify_all(); } void PhysicalCore::ClearInterrupt() { - guard->lock(); - interrupts[core_index].SetInterrupt(false); - guard->unlock(); + std::unique_lock lk{guard}; + is_interrupted = false; + arm_interface->ClearInterrupt(); + on_interrupt.notify_all(); } } // namespace Kernel diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index 16a032e89..2fc8d4be2 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h @@ -1,24 +1,19 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <cstddef> #include <memory> +#include <mutex> #include "core/arm/arm_interface.h" -namespace Common { -class SpinLock; -} - namespace Kernel { class KScheduler; } // namespace Kernel namespace Core { -class CPUInterruptHandler; class ExclusiveMonitor; class System; } // namespace Core @@ -27,15 +22,11 @@ namespace Kernel { class PhysicalCore { public: - PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_, - Core::CPUInterrupts& interrupts_); + PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_); ~PhysicalCore(); - PhysicalCore(const PhysicalCore&) = delete; - PhysicalCore& operator=(const PhysicalCore&) = delete; - - PhysicalCore(PhysicalCore&&) = default; - PhysicalCore& operator=(PhysicalCore&&) = delete; + YUZU_NON_COPYABLE(PhysicalCore); + YUZU_NON_MOVEABLE(PhysicalCore); /// Initialize the core for the specified parameters. void Initialize(bool is_64_bit); @@ -90,9 +81,11 @@ private: const std::size_t core_index; Core::System& system; Kernel::KScheduler& scheduler; - Core::CPUInterrupts& interrupts; - std::unique_ptr<Common::SpinLock> guard; + + std::mutex guard; + std::condition_variable on_interrupt; std::unique_ptr<Core::ARM_Interface> arm_interface; + bool is_interrupted; }; } // namespace Kernel diff --git a/src/core/hle/kernel/physical_memory.h b/src/core/hle/kernel/physical_memory.h index 7a0266780..253fa4563 100644 --- a/src/core/hle/kernel/physical_memory.h +++ b/src/core/hle/kernel/physical_memory.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp index 31a0867d3..773319ad8 100644 --- a/src/core/hle/kernel/process_capability.cpp +++ b/src/core/hle/kernel/process_capability.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <bit> @@ -69,9 +68,9 @@ u32 GetFlagBitOffset(CapabilityType type) { } // Anonymous namespace -ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities, - std::size_t num_capabilities, - KPageTable& page_table) { +Result ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities, + std::size_t num_capabilities, + KPageTable& page_table) { Clear(); // Allow all cores and priorities. @@ -82,9 +81,9 @@ ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabiliti return ParseCapabilities(capabilities, num_capabilities, page_table); } -ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities, - std::size_t num_capabilities, - KPageTable& page_table) { +Result ProcessCapabilities::InitializeForUserProcess(const u32* capabilities, + std::size_t num_capabilities, + KPageTable& page_table) { Clear(); return ParseCapabilities(capabilities, num_capabilities, page_table); @@ -108,9 +107,8 @@ void ProcessCapabilities::InitializeForMetadatalessProcess() { can_force_debug = true; } -ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities, - std::size_t num_capabilities, - KPageTable& page_table) { +Result ProcessCapabilities::ParseCapabilities(const u32* capabilities, std::size_t num_capabilities, + KPageTable& page_table) { u32 set_flags = 0; u32 set_svc_bits = 0; @@ -156,8 +154,8 @@ ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities, return ResultSuccess; } -ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, - u32 flag, KPageTable& page_table) { +Result ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag, + KPageTable& page_table) { const auto type = GetCapabilityType(flag); if (type == CapabilityType::Unset) { @@ -225,7 +223,7 @@ void ProcessCapabilities::Clear() { can_force_debug = false; } -ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) { +Result ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) { if (priority_mask != 0 || core_mask != 0) { LOG_ERROR(Kernel, "Core or priority mask are not zero! priority_mask={}, core_mask={}", priority_mask, core_mask); @@ -267,7 +265,7 @@ ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) { return ResultSuccess; } -ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) { +Result ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) { const u32 index = flags >> 29; const u32 svc_bit = 1U << index; @@ -291,23 +289,23 @@ ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) return ResultSuccess; } -ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags, - KPageTable& page_table) { +Result ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags, + KPageTable& page_table) { // TODO(Lioncache): Implement once the memory manager can handle this. return ResultSuccess; } -ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, KPageTable& page_table) { +Result ProcessCapabilities::HandleMapIOFlags(u32 flags, KPageTable& page_table) { // TODO(Lioncache): Implement once the memory manager can handle this. return ResultSuccess; } -ResultCode ProcessCapabilities::HandleMapRegionFlags(u32 flags, KPageTable& page_table) { +Result ProcessCapabilities::HandleMapRegionFlags(u32 flags, KPageTable& page_table) { // TODO(Lioncache): Implement once the memory manager can handle this. return ResultSuccess; } -ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) { +Result ProcessCapabilities::HandleInterruptFlags(u32 flags) { constexpr u32 interrupt_ignore_value = 0x3FF; const u32 interrupt0 = (flags >> 12) & 0x3FF; const u32 interrupt1 = (flags >> 22) & 0x3FF; @@ -334,7 +332,7 @@ ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) { return ResultSuccess; } -ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) { +Result ProcessCapabilities::HandleProgramTypeFlags(u32 flags) { const u32 reserved = flags >> 17; if (reserved != 0) { LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved); @@ -345,7 +343,7 @@ ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) { return ResultSuccess; } -ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) { +Result ProcessCapabilities::HandleKernelVersionFlags(u32 flags) { // Yes, the internal member variable is checked in the actual kernel here. // This might look odd for options that are only allowed to be initialized // just once, however the kernel has a separate initialization function for @@ -365,7 +363,7 @@ ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) { return ResultSuccess; } -ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) { +Result ProcessCapabilities::HandleHandleTableFlags(u32 flags) { const u32 reserved = flags >> 26; if (reserved != 0) { LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved); @@ -376,7 +374,7 @@ ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) { return ResultSuccess; } -ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) { +Result ProcessCapabilities::HandleDebugFlags(u32 flags) { const u32 reserved = flags >> 19; if (reserved != 0) { LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved); diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h index a9b44325b..ff05dc5ff 100644 --- a/src/core/hle/kernel/process_capability.h +++ b/src/core/hle/kernel/process_capability.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,7 +7,7 @@ #include "common/common_types.h" -union ResultCode; +union Result; namespace Kernel { @@ -87,8 +86,8 @@ public: /// @returns ResultSuccess if this capabilities instance was able to be initialized, /// otherwise, an error code upon failure. /// - ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities, - KPageTable& page_table); + Result InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities, + KPageTable& page_table); /// Initializes this process capabilities instance for a userland process. /// @@ -100,8 +99,8 @@ public: /// @returns ResultSuccess if this capabilities instance was able to be initialized, /// otherwise, an error code upon failure. /// - ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities, - KPageTable& page_table); + Result InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities, + KPageTable& page_table); /// Initializes this process capabilities instance for a process that does not /// have any metadata to parse. @@ -186,8 +185,8 @@ private: /// /// @return ResultSuccess if no errors occur, otherwise an error code. /// - ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities, - KPageTable& page_table); + Result ParseCapabilities(const u32* capabilities, std::size_t num_capabilities, + KPageTable& page_table); /// Attempts to parse a capability descriptor that is only represented by a /// single flag set. @@ -201,8 +200,8 @@ private: /// /// @return ResultSuccess if no errors occurred, otherwise an error code. /// - ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag, - KPageTable& page_table); + Result ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag, + KPageTable& page_table); /// Clears the internal state of this process capability instance. Necessary, /// to have a sane starting point due to us allowing running executables without @@ -220,34 +219,34 @@ private: void Clear(); /// Handles flags related to the priority and core number capability flags. - ResultCode HandlePriorityCoreNumFlags(u32 flags); + Result HandlePriorityCoreNumFlags(u32 flags); /// Handles flags related to determining the allowable SVC mask. - ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags); + Result HandleSyscallFlags(u32& set_svc_bits, u32 flags); /// Handles flags related to mapping physical memory pages. - ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, KPageTable& page_table); + Result HandleMapPhysicalFlags(u32 flags, u32 size_flags, KPageTable& page_table); /// Handles flags related to mapping IO pages. - ResultCode HandleMapIOFlags(u32 flags, KPageTable& page_table); + Result HandleMapIOFlags(u32 flags, KPageTable& page_table); /// Handles flags related to mapping physical memory regions. - ResultCode HandleMapRegionFlags(u32 flags, KPageTable& page_table); + Result HandleMapRegionFlags(u32 flags, KPageTable& page_table); /// Handles flags related to the interrupt capability flags. - ResultCode HandleInterruptFlags(u32 flags); + Result HandleInterruptFlags(u32 flags); /// Handles flags related to the program type. - ResultCode HandleProgramTypeFlags(u32 flags); + Result HandleProgramTypeFlags(u32 flags); /// Handles flags related to the handle table size. - ResultCode HandleHandleTableFlags(u32 flags); + Result HandleHandleTableFlags(u32 flags); /// Handles flags related to the kernel version capability flags. - ResultCode HandleKernelVersionFlags(u32 flags); + Result HandleKernelVersionFlags(u32 flags); /// Handles flags related to debug-specific capabilities. - ResultCode HandleDebugFlags(u32 flags); + Result HandleDebugFlags(u32 flags); SyscallCapabilities svc_capabilities; InterruptCapabilities interrupt_capabilities; diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index 4eb3a5988..d23d76706 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <condition_variable> #include <functional> @@ -37,7 +36,7 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std : service_name{name} { for (std::size_t i = 0; i < num_threads; ++i) { threads.emplace_back([this, &kernel](std::stop_token stop_token) { - Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str()); + Common::SetCurrentThreadName(std::string{service_name}.c_str()); // Wait for first request before trying to acquire a render context { @@ -49,12 +48,9 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std return; } + // Allocate a dummy guest thread for this host thread. kernel.RegisterHostThread(); - // Ensure the dummy thread allocated for this host thread is closed on exit. - auto* dummy_thread = kernel.GetCurrentEmuThread(); - SCOPE_EXIT({ dummy_thread->Close(); }); - while (true) { std::function<void()> task; diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h index 6a7fd7c56..c5896f2bd 100644 --- a/src/core/hle/kernel/service_thread.h +++ b/src/core/hle/kernel/service_thread.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/slab_helpers.h b/src/core/hle/kernel/slab_helpers.h index f1c11256e..299a981a8 100644 --- a/src/core/hle/kernel/slab_helpers.h +++ b/src/core/hle/kernel/slab_helpers.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -59,7 +58,7 @@ class KAutoObjectWithSlabHeapAndContainer : public Base { private: static Derived* Allocate(KernelCore& kernel) { - return kernel.SlabHeap<Derived>().AllocateWithKernel(kernel); + return kernel.SlabHeap<Derived>().Allocate(kernel); } static void Free(KernelCore& kernel, Derived* obj) { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 9387373c1..27e5a805d 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cinttypes> @@ -16,6 +15,7 @@ #include "common/scope_exit.h" #include "core/core.h" #include "core/core_timing.h" +#include "core/debugger/debugger.h" #include "core/hle/kernel/k_client_port.h" #include "core/hle/kernel/k_client_session.h" #include "core/hle/kernel/k_code_memory.h" @@ -58,8 +58,8 @@ constexpr bool IsValidAddressRange(VAddr address, u64 size) { // Helper function that performs the common sanity checks for svcMapMemory // and svcUnmapMemory. This is doable, as both functions perform their sanitizing // in the same order. -ResultCode MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr, - u64 size) { +Result MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr, + u64 size) { if (!Common::Is4KBAligned(dst_addr)) { LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr); return ResultInvalidAddress; @@ -135,7 +135,7 @@ enum class ResourceLimitValueType { } // Anonymous namespace /// Set the process heap to a given Size. It can both extend and shrink the heap. -static ResultCode SetHeapSize(Core::System& system, VAddr* out_address, u64 size) { +static Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size) { LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size); // Validate size. @@ -148,9 +148,9 @@ static ResultCode SetHeapSize(Core::System& system, VAddr* out_address, u64 size return ResultSuccess; } -static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) { +static Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) { VAddr temp_heap_addr{}; - const ResultCode result{SetHeapSize(system, &temp_heap_addr, heap_size)}; + const Result result{SetHeapSize(system, &temp_heap_addr, heap_size)}; *heap_addr = static_cast<u32>(temp_heap_addr); return result; } @@ -166,8 +166,8 @@ constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) { } } -static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 size, - MemoryPermission perm) { +static Result SetMemoryPermission(Core::System& system, VAddr address, u64 size, + MemoryPermission perm) { LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size, perm); @@ -188,8 +188,8 @@ static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 s return page_table.SetMemoryPermission(address, size, perm); } -static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, - u32 attr) { +static Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, + u32 attr) { LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, size, mask, attr); @@ -213,19 +213,19 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si return page_table.SetMemoryAttribute(address, size, mask, attr); } -static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, - u32 attr) { +static Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, + u32 attr) { return SetMemoryAttribute(system, address, size, mask, attr); } /// Maps a memory range into a different range. -static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { +static Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, src_addr, size); auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; - if (const ResultCode result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; + if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; result.IsError()) { return result; } @@ -233,18 +233,18 @@ static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr return page_table.MapMemory(dst_addr, src_addr, size); } -static ResultCode MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { +static Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { return MapMemory(system, dst_addr, src_addr, size); } /// Unmaps a region that was previously mapped with svcMapMemory -static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { +static Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, src_addr, size); auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; - if (const ResultCode result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; + if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; result.IsError()) { return result; } @@ -252,12 +252,12 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad return page_table.UnmapMemory(dst_addr, src_addr, size); } -static ResultCode UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { +static Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { return UnmapMemory(system, dst_addr, src_addr, size); } /// Connect to an OS service given the port name, returns the handle to the port to out -static ResultCode ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) { +static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) { auto& memory = system.Memory(); if (!memory.IsValidVirtualAddress(port_name_address)) { LOG_ERROR(Kernel_SVC, @@ -307,14 +307,14 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out, VAddr po return ResultSuccess; } -static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle, - u32 port_name_address) { +static Result ConnectToNamedPort32(Core::System& system, Handle* out_handle, + u32 port_name_address) { return ConnectToNamedPort(system, out_handle, port_name_address); } /// Makes a blocking IPC call to an OS service. -static ResultCode SendSyncRequest(Core::System& system, Handle handle) { +static Result SendSyncRequest(Core::System& system, Handle handle) { auto& kernel = system.Kernel(); // Create the wait queue. @@ -327,7 +327,6 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); - auto thread = kernel.CurrentScheduler()->GetCurrentThread(); { KScopedSchedulerLock lock(kernel); @@ -337,15 +336,15 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming()); } - return thread->GetWaitResult(); + return GetCurrentThread(kernel).GetWaitResult(); } -static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { +static Result SendSyncRequest32(Core::System& system, Handle handle) { return SendSyncRequest(system, handle); } /// Get the ID for the specified thread. -static ResultCode GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) { +static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) { // Get the thread from its handle. KScopedAutoObject thread = system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); @@ -356,10 +355,10 @@ static ResultCode GetThreadId(Core::System& system, u64* out_thread_id, Handle t return ResultSuccess; } -static ResultCode GetThreadId32(Core::System& system, u32* out_thread_id_low, - u32* out_thread_id_high, Handle thread_handle) { +static Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high, + Handle thread_handle) { u64 out_thread_id{}; - const ResultCode result{GetThreadId(system, &out_thread_id, thread_handle)}; + const Result result{GetThreadId(system, &out_thread_id, thread_handle)}; *out_thread_id_low = static_cast<u32>(out_thread_id >> 32); *out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max()); @@ -368,7 +367,7 @@ static ResultCode GetThreadId32(Core::System& system, u32* out_thread_id_low, } /// Gets the ID of the specified process or a specified thread's owning process. -static ResultCode GetProcessId(Core::System& system, u64* out_process_id, Handle handle) { +static Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) { LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle); // Get the object from the handle table. @@ -399,8 +398,8 @@ static ResultCode GetProcessId(Core::System& system, u64* out_process_id, Handle return ResultSuccess; } -static ResultCode GetProcessId32(Core::System& system, u32* out_process_id_low, - u32* out_process_id_high, Handle handle) { +static Result GetProcessId32(Core::System& system, u32* out_process_id_low, + u32* out_process_id_high, Handle handle) { u64 out_process_id{}; const auto result = GetProcessId(system, &out_process_id, handle); *out_process_id_low = static_cast<u32>(out_process_id); @@ -409,8 +408,8 @@ static ResultCode GetProcessId32(Core::System& system, u32* out_process_id_low, } /// Wait for the given handles to synchronize, timeout after the specified nanoseconds -static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, - s32 num_handles, s64 nano_seconds) { +static Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, + s32 num_handles, s64 nano_seconds) { LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}", handles_address, num_handles, nano_seconds); @@ -445,14 +444,14 @@ static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr ha nano_seconds); } -static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, - s32 num_handles, u32 timeout_high, s32* index) { +static Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, + s32 num_handles, u32 timeout_high, s32* index) { const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)}; return WaitSynchronization(system, index, handles_address, num_handles, nano_seconds); } /// Resumes a thread waiting on WaitSynchronization -static ResultCode CancelSynchronization(Core::System& system, Handle handle) { +static Result CancelSynchronization(Core::System& system, Handle handle) { LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle); // Get the thread from its handle. @@ -465,13 +464,12 @@ static ResultCode CancelSynchronization(Core::System& system, Handle handle) { return ResultSuccess; } -static ResultCode CancelSynchronization32(Core::System& system, Handle handle) { +static Result CancelSynchronization32(Core::System& system, Handle handle) { return CancelSynchronization(system, handle); } /// Attempts to locks a mutex -static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, - u32 tag) { +static Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag) { LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}", thread_handle, address, tag); @@ -489,13 +487,12 @@ static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAdd return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag); } -static ResultCode ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, - u32 tag) { +static Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag) { return ArbitrateLock(system, thread_handle, address, tag); } /// Unlock a mutex -static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) { +static Result ArbitrateUnlock(Core::System& system, VAddr address) { LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address); // Validate the input address. @@ -513,7 +510,7 @@ static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) { return system.Kernel().CurrentProcess()->SignalToAddress(address); } -static ResultCode ArbitrateUnlock32(Core::System& system, u32 address) { +static Result ArbitrateUnlock32(Core::System& system, u32 address) { return ArbitrateUnlock(system, address); } @@ -624,10 +621,16 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { handle_debug_buffer(info1, info2); - auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); + auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); const auto thread_processor_id = current_thread->GetActiveCore(); system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); } + + if (system.DebuggerEnabled()) { + auto* thread = system.Kernel().GetCurrentEmuThread(); + system.GetDebugger().NotifyThreadStopped(thread); + thread->RequestSuspend(Kernel::SuspendType::Debug); + } } static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) { @@ -645,9 +648,13 @@ static void OutputDebugString(Core::System& system, VAddr address, u64 len) { LOG_DEBUG(Debug_Emulated, "{}", str); } +static void OutputDebugString32(Core::System& system, u32 address, u32 len) { + OutputDebugString(system, address, len); +} + /// Gets system/memory information for the current process -static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle, - u64 info_sub_id) { +static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle 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); @@ -682,6 +689,9 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle // 6.0.0+ TotalPhysicalMemoryAvailableWithoutSystemResource = 21, TotalPhysicalMemoryUsedWithoutSystemResource = 22, + + // Homebrew only + MesosphereCurrentProcess = 65001, }; const auto info_id_type = static_cast<GetInfoType>(info_id); @@ -874,10 +884,10 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle const auto& core_timing = system.CoreTiming(); const auto& scheduler = *system.Kernel().CurrentScheduler(); - const auto* const current_thread = scheduler.GetCurrentThread(); + const auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); const bool same_thread = current_thread == thread.GetPointerUnsafe(); - const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks(); + const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime(); u64 out_ticks = 0; if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { const u64 thread_ticks = current_thread->GetCpuTime(); @@ -896,7 +906,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle // Verify the requested core is valid. const bool core_valid = - (info_sub_id == static_cast<u64>(-1ULL)) || + (info_sub_id == 0xFFFFFFFFFFFFFFFF) || (info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex())); R_UNLESS(core_valid, ResultInvalidCombination); @@ -904,18 +914,39 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime(); return ResultSuccess; } + case GetInfoType::MesosphereCurrentProcess: { + // Verify the input handle is invalid. + R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); + + // Verify the sub-type is valid. + R_UNLESS(info_sub_id == 0, ResultInvalidCombination); + + // Get the handle table. + KProcess* current_process = system.Kernel().CurrentProcess(); + KHandleTable& handle_table = current_process->GetHandleTable(); + + // Get a new handle for the current process. + Handle tmp; + R_TRY(handle_table.Add(&tmp, current_process)); + + // Set the output. + *result = tmp; + + // We succeeded. + return ResultSuccess; + } default: LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id); return ResultInvalidEnumValue; } } -static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low, - u32 info_id, u32 handle, u32 sub_id_high) { +static Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low, + u32 info_id, u32 handle, u32 sub_id_high) { const u64 sub_id{u64{sub_id_low} | (u64{sub_id_high} << 32)}; u64 res_value{}; - const ResultCode result{GetInfo(system, &res_value, info_id, handle, sub_id)}; + const Result result{GetInfo(system, &res_value, info_id, handle, sub_id)}; *result_high = static_cast<u32>(res_value >> 32); *result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max()); @@ -923,7 +954,7 @@ static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_h } /// Maps memory at a desired address -static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { +static Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); if (!Common::Is4KBAligned(addr)) { @@ -971,12 +1002,12 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) return page_table.MapPhysicalMemory(addr, size); } -static ResultCode MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { +static Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { return MapPhysicalMemory(system, addr, size); } /// Unmaps memory previously mapped via MapPhysicalMemory -static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { +static Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); if (!Common::Is4KBAligned(addr)) { @@ -1024,13 +1055,13 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size return page_table.UnmapPhysicalMemory(addr, size); } -static ResultCode UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { +static Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { return UnmapPhysicalMemory(system, addr, size); } /// Sets the thread activity -static ResultCode SetThreadActivity(Core::System& system, Handle thread_handle, - ThreadActivity thread_activity) { +static Result SetThreadActivity(Core::System& system, Handle thread_handle, + ThreadActivity thread_activity) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle, thread_activity); @@ -1055,13 +1086,13 @@ static ResultCode SetThreadActivity(Core::System& system, Handle thread_handle, return ResultSuccess; } -static ResultCode SetThreadActivity32(Core::System& system, Handle thread_handle, - Svc::ThreadActivity thread_activity) { +static Result SetThreadActivity32(Core::System& system, Handle thread_handle, + Svc::ThreadActivity thread_activity) { return SetThreadActivity(system, thread_handle, thread_activity); } /// Gets the thread context -static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) { +static Result GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) { LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context, thread_handle); @@ -1093,7 +1124,7 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand if (thread->GetRawState() != ThreadState::Runnable) { bool current = false; for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { - if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetCurrentThread()) { + if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) { current = true; break; } @@ -1118,12 +1149,12 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand return ResultSuccess; } -static ResultCode GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) { +static Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) { return GetThreadContext(system, out_context, thread_handle); } /// Gets the priority for the specified thread -static ResultCode GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) { +static Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) { LOG_TRACE(Kernel_SVC, "called"); // Get the thread from its handle. @@ -1136,12 +1167,12 @@ static ResultCode GetThreadPriority(Core::System& system, u32* out_priority, Han return ResultSuccess; } -static ResultCode GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) { +static Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) { return GetThreadPriority(system, out_priority, handle); } /// Sets the priority for the specified thread -static ResultCode SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) { +static Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) { // Get the current process. KProcess& process = *system.Kernel().CurrentProcess(); @@ -1159,7 +1190,7 @@ static ResultCode SetThreadPriority(Core::System& system, Handle thread_handle, return ResultSuccess; } -static ResultCode SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) { +static Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) { return SetThreadPriority(system, thread_handle, priority); } @@ -1219,8 +1250,8 @@ constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission p } // Anonymous namespace -static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, - u64 size, Svc::MemoryPermission map_perm) { +static Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size, + Svc::MemoryPermission map_perm) { LOG_TRACE(Kernel_SVC, "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", shmem_handle, address, size, map_perm); @@ -1260,13 +1291,13 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAd return ResultSuccess; } -static ResultCode MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, - u32 size, Svc::MemoryPermission map_perm) { +static Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size, + Svc::MemoryPermission map_perm) { return MapSharedMemory(system, shmem_handle, address, size, map_perm); } -static ResultCode UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, - u64 size) { +static Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, + u64 size) { // Validate the address/size. R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); @@ -1293,13 +1324,13 @@ static ResultCode UnmapSharedMemory(Core::System& system, Handle shmem_handle, V return ResultSuccess; } -static ResultCode UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, - u32 size) { +static Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, + u32 size) { return UnmapSharedMemory(system, shmem_handle, address, size); } -static ResultCode SetProcessMemoryPermission(Core::System& system, Handle process_handle, - VAddr address, u64 size, Svc::MemoryPermission perm) { +static Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address, + u64 size, Svc::MemoryPermission perm) { LOG_TRACE(Kernel_SVC, "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", process_handle, address, size, perm); @@ -1328,8 +1359,8 @@ static ResultCode SetProcessMemoryPermission(Core::System& system, Handle proces return page_table.SetProcessMemoryPermission(address, size, perm); } -static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, - VAddr src_address, u64 size) { +static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, + VAddr src_address, u64 size) { LOG_TRACE(Kernel_SVC, "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", dst_address, process_handle, src_address, size); @@ -1358,8 +1389,11 @@ static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Hand ResultInvalidMemoryRegion); // Create a new page group. - KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address); - KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages()); + KPageGroup pg; + R_TRY(src_pt.MakeAndOpenPageGroup( + std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess, + KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None, + KMemoryAttribute::All, KMemoryAttribute::None)); // Map the group. R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode, @@ -1368,8 +1402,8 @@ static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Hand return ResultSuccess; } -static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, - VAddr src_address, u64 size) { +static Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, + VAddr src_address, u64 size) { LOG_TRACE(Kernel_SVC, "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", dst_address, process_handle, src_address, size); @@ -1403,9 +1437,9 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha return ResultSuccess; } -static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { - LOG_TRACE(Kernel_SVC, "called, handle_out=0x{:X}, address=0x{:X}, size=0x{:X}", - static_cast<void*>(out), address, size); +static Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { + LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size); + // Get kernel instance. auto& kernel = system.Kernel(); @@ -1438,8 +1472,12 @@ static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr addr return ResultSuccess; } -static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, - VAddr address, size_t size, Svc::MemoryPermission perm) { +static Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) { + return CreateCodeMemory(system, out, address, size); +} + +static Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, + VAddr address, size_t size, Svc::MemoryPermission perm) { LOG_TRACE(Kernel_SVC, "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, " @@ -1517,9 +1555,13 @@ static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_han return ResultSuccess; } -static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, - VAddr page_info_address, Handle process_handle, - VAddr address) { +static Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation, + u64 address, u64 size, Svc::MemoryPermission perm) { + return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm); +} + +static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address, + VAddr page_info_address, Handle process_handle, VAddr address) { LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); @@ -1547,8 +1589,8 @@ static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_add return ResultSuccess; } -static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address, - VAddr page_info_address, VAddr query_address) { +static Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address, + VAddr query_address) { LOG_TRACE(Kernel_SVC, "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, " "query_address=0x{:016X}", @@ -1558,13 +1600,13 @@ static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address, query_address); } -static ResultCode QueryMemory32(Core::System& system, u32 memory_info_address, - u32 page_info_address, u32 query_address) { +static Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address, + u32 query_address) { return QueryMemory(system, memory_info_address, page_info_address, query_address); } -static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, - u64 src_address, u64 size) { +static Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, + u64 src_address, u64 size) { LOG_DEBUG(Kernel_SVC, "called. process_handle=0x{:08X}, dst_address=0x{:016X}, " "src_address=0x{:016X}, size=0x{:016X}", @@ -1631,8 +1673,8 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand return page_table.MapCodeMemory(dst_address, src_address, size); } -static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, - u64 dst_address, u64 src_address, u64 size) { +static Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, + u64 src_address, u64 size) { LOG_DEBUG(Kernel_SVC, "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, " "size=0x{:016X}", @@ -1650,7 +1692,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha return ResultInvalidAddress; } - if (size == 0 || Common::Is4KBAligned(size)) { + if (size == 0 || !Common::Is4KBAligned(size)) { LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); return ResultInvalidSize; } @@ -1696,17 +1738,19 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha return ResultInvalidMemoryRegion; } - return page_table.UnmapCodeMemory(dst_address, src_address, size); + return page_table.UnmapCodeMemory(dst_address, src_address, size, + KPageTable::ICacheInvalidationStrategy::InvalidateAll); } /// Exits the current process static void ExitProcess(Core::System& system) { auto* current_process = system.Kernel().CurrentProcess(); - UNIMPLEMENTED(); LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, "Process has already exited"); + + system.Exit(); } static void ExitProcess32(Core::System& system) { @@ -1722,8 +1766,8 @@ constexpr bool IsValidVirtualCoreId(int32_t core_id) { } // Anonymous namespace /// Creates a new thread -static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, - VAddr stack_bottom, u32 priority, s32 core_id) { +static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, + VAddr stack_bottom, u32 priority, s32 core_id) { LOG_DEBUG(Kernel_SVC, "called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, " "priority=0x{:08X}, core_id=0x{:08X}", @@ -1794,13 +1838,13 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e return ResultSuccess; } -static ResultCode CreateThread32(Core::System& system, Handle* out_handle, u32 priority, - u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) { +static Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority, + u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) { return CreateThread(system, out_handle, entry_point, arg, stack_top, priority, processor_id); } /// Starts the thread for the provided handle -static ResultCode StartThread(Core::System& system, Handle thread_handle) { +static Result StartThread(Core::System& system, Handle thread_handle) { LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle); // Get the thread from its handle. @@ -1818,7 +1862,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) { return ResultSuccess; } -static ResultCode StartThread32(Core::System& system, Handle thread_handle) { +static Result StartThread32(Core::System& system, Handle thread_handle) { return StartThread(system, thread_handle); } @@ -1826,7 +1870,7 @@ static ResultCode StartThread32(Core::System& system, Handle thread_handle) { static void ExitThread(Core::System& system) { LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); - auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); + auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); system.GlobalSchedulerContext().RemoveThread(current_thread); current_thread->Exit(); system.Kernel().UnregisterInUseObject(current_thread); @@ -1859,7 +1903,7 @@ static void SleepThread(Core::System& system, s64 nanoseconds) { KScheduler::YieldToAnyThread(kernel); } else { // Nintendo does nothing at all if an otherwise invalid value is passed. - UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); + ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds); } } @@ -1869,8 +1913,8 @@ static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanosec } /// Wait process wide key atomic -static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, - u32 tag, s64 timeout_ns) { +static Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag, + s64 timeout_ns) { LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address, cv_key, tag, timeout_ns); @@ -1905,8 +1949,8 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address, address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout); } -static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag, - u32 timeout_ns_low, u32 timeout_ns_high) { +static Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag, + u32 timeout_ns_low, u32 timeout_ns_high) { const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns); } @@ -1951,8 +1995,8 @@ constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) { } // namespace // Wait for an address (via Address Arbiter) -static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type, - s32 value, s64 timeout_ns) { +static Result WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type, + s32 value, s64 timeout_ns) { LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}", address, arb_type, value, timeout_ns); @@ -1989,15 +2033,15 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::Arbit return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout); } -static ResultCode WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type, - s32 value, u32 timeout_ns_low, u32 timeout_ns_high) { +static Result WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type, + s32 value, u32 timeout_ns_low, u32 timeout_ns_high) { const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); return WaitForAddress(system, address, arb_type, value, timeout); } // Signals to an address (via Address Arbiter) -static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type, - s32 value, s32 count) { +static Result SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type, + s32 value, s32 count) { LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}", address, signal_type, value, count); @@ -2038,8 +2082,8 @@ static void SynchronizePreemptionState(Core::System& system) { } } -static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type, - s32 value, s32 count) { +static Result SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type, + s32 value, s32 count) { return SignalToAddress(system, address, signal_type, value, count); } @@ -2077,7 +2121,7 @@ static void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) } /// Close a handle -static ResultCode CloseHandle(Core::System& system, Handle handle) { +static Result CloseHandle(Core::System& system, Handle handle) { LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); // Remove the handle. @@ -2087,12 +2131,12 @@ static ResultCode CloseHandle(Core::System& system, Handle handle) { return ResultSuccess; } -static ResultCode CloseHandle32(Core::System& system, Handle handle) { +static Result CloseHandle32(Core::System& system, Handle handle) { return CloseHandle(system, handle); } /// Clears the signaled state of an event or process. -static ResultCode ResetSignal(Core::System& system, Handle handle) { +static Result ResetSignal(Core::System& system, Handle handle) { LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle); // Get the current handle table. @@ -2119,7 +2163,7 @@ static ResultCode ResetSignal(Core::System& system, Handle handle) { return ResultInvalidHandle; } -static ResultCode ResetSignal32(Core::System& system, Handle handle) { +static Result ResetSignal32(Core::System& system, Handle handle) { return ResetSignal(system, handle); } @@ -2139,8 +2183,8 @@ constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) { } // Anonymous namespace /// Creates a TransferMemory object -static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size, - MemoryPermission map_perm) { +static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size, + MemoryPermission map_perm) { auto& kernel = system.Kernel(); // Validate the size. @@ -2186,13 +2230,13 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr return ResultSuccess; } -static ResultCode CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size, - MemoryPermission map_perm) { +static Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size, + MemoryPermission map_perm) { return CreateTransferMemory(system, out, address, size, map_perm); } -static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id, - u64* out_affinity_mask) { +static Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id, + u64* out_affinity_mask) { LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); // Get the thread from its handle. @@ -2206,8 +2250,8 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, return ResultSuccess; } -static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id, - u32* out_affinity_mask_low, u32* out_affinity_mask_high) { +static Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id, + u32* out_affinity_mask_low, u32* out_affinity_mask_high) { u64 out_affinity_mask{}; const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask); *out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32); @@ -2215,8 +2259,8 @@ static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle return result; } -static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id, - u64 affinity_mask) { +static Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id, + u64 affinity_mask) { // Determine the core id/affinity mask. if (core_id == IdealCoreUseProcessValue) { core_id = system.Kernel().CurrentProcess()->GetIdealCoreId(); @@ -2247,13 +2291,13 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, return ResultSuccess; } -static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id, - u32 affinity_mask_low, u32 affinity_mask_high) { +static Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id, + u32 affinity_mask_low, u32 affinity_mask_high) { const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32); return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask); } -static ResultCode SignalEvent(Core::System& system, Handle event_handle) { +static Result SignalEvent(Core::System& system, Handle event_handle) { LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); // Get the current handle table. @@ -2266,11 +2310,11 @@ static ResultCode SignalEvent(Core::System& system, Handle event_handle) { return writable_event->Signal(); } -static ResultCode SignalEvent32(Core::System& system, Handle event_handle) { +static Result SignalEvent32(Core::System& system, Handle event_handle) { return SignalEvent(system, event_handle); } -static ResultCode ClearEvent(Core::System& system, Handle event_handle) { +static Result ClearEvent(Core::System& system, Handle event_handle) { LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); // Get the current handle table. @@ -2297,11 +2341,11 @@ static ResultCode ClearEvent(Core::System& system, Handle event_handle) { return ResultInvalidHandle; } -static ResultCode ClearEvent32(Core::System& system, Handle event_handle) { +static Result ClearEvent32(Core::System& system, Handle event_handle) { return ClearEvent(system, event_handle); } -static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) { +static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) { LOG_DEBUG(Kernel_SVC, "called"); // Get the kernel reference and handle table. @@ -2318,7 +2362,7 @@ static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* o R_UNLESS(event != nullptr, ResultOutOfResource); // Initialize the event. - event->Initialize("CreateEvent"); + event->Initialize("CreateEvent", kernel.CurrentProcess()); // Commit the thread reservation. event_reservation.Commit(); @@ -2346,11 +2390,11 @@ static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* o return ResultSuccess; } -static ResultCode CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) { +static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) { return CreateEvent(system, out_write, out_read); } -static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { +static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); // This function currently only allows retrieving a process' status. @@ -2376,7 +2420,7 @@ static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_ return ResultSuccess; } -static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) { +static Result CreateResourceLimit(Core::System& system, Handle* out_handle) { LOG_DEBUG(Kernel_SVC, "called"); // Create a new resource limit. @@ -2399,9 +2443,8 @@ static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) return ResultSuccess; } -static ResultCode GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value, - Handle resource_limit_handle, - LimitableResource which) { +static Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value, + Handle resource_limit_handle, LimitableResource which) { LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle, which); @@ -2420,9 +2463,8 @@ static ResultCode GetResourceLimitLimitValue(Core::System& system, u64* out_limi return ResultSuccess; } -static ResultCode GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value, - Handle resource_limit_handle, - LimitableResource which) { +static Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value, + Handle resource_limit_handle, LimitableResource which) { LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle, which); @@ -2441,8 +2483,8 @@ static ResultCode GetResourceLimitCurrentValue(Core::System& system, u64* out_cu return ResultSuccess; } -static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle, - LimitableResource which, u64 limit_value) { +static Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle, + LimitableResource which, u64 limit_value) { LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}, limit_value={}", resource_limit_handle, which, limit_value); @@ -2461,8 +2503,8 @@ static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resour return ResultSuccess; } -static ResultCode GetProcessList(Core::System& system, u32* out_num_processes, - VAddr out_process_ids, u32 out_process_ids_size) { +static Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids, + u32 out_process_ids_size) { LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}", out_process_ids, out_process_ids_size); @@ -2498,8 +2540,8 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes, return ResultSuccess; } -static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, - u32 out_thread_ids_size, Handle debug_handle) { +static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, + u32 out_thread_ids_size, Handle debug_handle) { // TODO: Handle this case when debug events are supported. UNIMPLEMENTED_IF(debug_handle != InvalidHandle); @@ -2513,7 +2555,7 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd return ResultOutOfRange; } - const auto* const current_process = system.Kernel().CurrentProcess(); + auto* const current_process = system.Kernel().CurrentProcess(); const auto total_copy_size = out_thread_ids_size * sizeof(u64); if (out_thread_ids_size > 0 && @@ -2538,9 +2580,9 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd return ResultSuccess; } -static ResultCode FlushProcessDataCache32([[maybe_unused]] Core::System& system, - [[maybe_unused]] Handle handle, - [[maybe_unused]] u32 address, [[maybe_unused]] u32 size) { +static Result FlushProcessDataCache32([[maybe_unused]] Core::System& system, + [[maybe_unused]] Handle handle, [[maybe_unused]] u32 address, + [[maybe_unused]] u32 size) { // Note(Blinkhawk): For emulation purposes of the data cache this is mostly a no-op, // as all emulation is done in the same cache level in host architecture, thus data cache // does not need flushing. @@ -2559,9 +2601,9 @@ struct FunctionDef { } // namespace static const FunctionDef SVC_Table_32[] = { - {0x00, nullptr, "Unknown"}, + {0x00, nullptr, "Unknown0"}, {0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"}, - {0x02, nullptr, "Unknown"}, + {0x02, nullptr, "SetMemoryPermission32"}, {0x03, SvcWrap32<SetMemoryAttribute32>, "SetMemoryAttribute32"}, {0x04, SvcWrap32<MapMemory32>, "MapMemory32"}, {0x05, SvcWrap32<UnmapMemory32>, "UnmapMemory32"}, @@ -2591,97 +2633,97 @@ static const FunctionDef SVC_Table_32[] = { {0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"}, {0x1e, SvcWrap32<GetSystemTick32>, "GetSystemTick32"}, {0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"}, - {0x20, nullptr, "Unknown"}, + {0x20, nullptr, "SendSyncRequestLight32"}, {0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"}, {0x22, nullptr, "SendSyncRequestWithUserBuffer32"}, - {0x23, nullptr, "Unknown"}, + {0x23, nullptr, "SendAsyncRequestWithUserBuffer32"}, {0x24, SvcWrap32<GetProcessId32>, "GetProcessId32"}, {0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"}, {0x26, SvcWrap32<Break32>, "Break32"}, - {0x27, nullptr, "OutputDebugString32"}, - {0x28, nullptr, "Unknown"}, + {0x27, SvcWrap32<OutputDebugString32>, "OutputDebugString32"}, + {0x28, nullptr, "ReturnFromException32"}, {0x29, SvcWrap32<GetInfo32>, "GetInfo32"}, - {0x2a, nullptr, "Unknown"}, - {0x2b, nullptr, "Unknown"}, + {0x2a, nullptr, "FlushEntireDataCache32"}, + {0x2b, nullptr, "FlushDataCache32"}, {0x2c, SvcWrap32<MapPhysicalMemory32>, "MapPhysicalMemory32"}, {0x2d, SvcWrap32<UnmapPhysicalMemory32>, "UnmapPhysicalMemory32"}, - {0x2e, nullptr, "Unknown"}, - {0x2f, nullptr, "Unknown"}, - {0x30, nullptr, "Unknown"}, - {0x31, nullptr, "Unknown"}, + {0x2e, nullptr, "GetDebugFutureThreadInfo32"}, + {0x2f, nullptr, "GetLastThreadInfo32"}, + {0x30, nullptr, "GetResourceLimitLimitValue32"}, + {0x31, nullptr, "GetResourceLimitCurrentValue32"}, {0x32, SvcWrap32<SetThreadActivity32>, "SetThreadActivity32"}, {0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"}, {0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"}, {0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"}, {0x36, SvcWrap32<SynchronizePreemptionState>, "SynchronizePreemptionState32"}, - {0x37, nullptr, "Unknown"}, - {0x38, nullptr, "Unknown"}, - {0x39, nullptr, "Unknown"}, - {0x3a, nullptr, "Unknown"}, - {0x3b, nullptr, "Unknown"}, - {0x3c, nullptr, "Unknown"}, - {0x3d, nullptr, "Unknown"}, - {0x3e, nullptr, "Unknown"}, - {0x3f, nullptr, "Unknown"}, + {0x37, nullptr, "GetResourceLimitPeakValue32"}, + {0x38, nullptr, "Unknown38"}, + {0x39, nullptr, "CreateIoPool32"}, + {0x3a, nullptr, "CreateIoRegion32"}, + {0x3b, nullptr, "Unknown3b"}, + {0x3c, nullptr, "KernelDebug32"}, + {0x3d, nullptr, "ChangeKernelTraceState32"}, + {0x3e, nullptr, "Unknown3e"}, + {0x3f, nullptr, "Unknown3f"}, {0x40, nullptr, "CreateSession32"}, {0x41, nullptr, "AcceptSession32"}, - {0x42, nullptr, "Unknown"}, + {0x42, nullptr, "ReplyAndReceiveLight32"}, {0x43, nullptr, "ReplyAndReceive32"}, - {0x44, nullptr, "Unknown"}, + {0x44, nullptr, "ReplyAndReceiveWithUserBuffer32"}, {0x45, SvcWrap32<CreateEvent32>, "CreateEvent32"}, - {0x46, nullptr, "Unknown"}, - {0x47, nullptr, "Unknown"}, - {0x48, nullptr, "Unknown"}, - {0x49, nullptr, "Unknown"}, - {0x4a, nullptr, "Unknown"}, - {0x4b, nullptr, "Unknown"}, - {0x4c, nullptr, "Unknown"}, - {0x4d, nullptr, "Unknown"}, - {0x4e, nullptr, "Unknown"}, - {0x4f, nullptr, "Unknown"}, - {0x50, nullptr, "Unknown"}, - {0x51, nullptr, "Unknown"}, - {0x52, nullptr, "Unknown"}, - {0x53, nullptr, "Unknown"}, - {0x54, nullptr, "Unknown"}, - {0x55, nullptr, "Unknown"}, - {0x56, nullptr, "Unknown"}, - {0x57, nullptr, "Unknown"}, - {0x58, nullptr, "Unknown"}, - {0x59, nullptr, "Unknown"}, - {0x5a, nullptr, "Unknown"}, - {0x5b, nullptr, "Unknown"}, - {0x5c, nullptr, "Unknown"}, - {0x5d, nullptr, "Unknown"}, - {0x5e, nullptr, "Unknown"}, + {0x46, nullptr, "MapIoRegion32"}, + {0x47, nullptr, "UnmapIoRegion32"}, + {0x48, nullptr, "MapPhysicalMemoryUnsafe32"}, + {0x49, nullptr, "UnmapPhysicalMemoryUnsafe32"}, + {0x4a, nullptr, "SetUnsafeLimit32"}, + {0x4b, SvcWrap32<CreateCodeMemory32>, "CreateCodeMemory32"}, + {0x4c, SvcWrap32<ControlCodeMemory32>, "ControlCodeMemory32"}, + {0x4d, nullptr, "SleepSystem32"}, + {0x4e, nullptr, "ReadWriteRegister32"}, + {0x4f, nullptr, "SetProcessActivity32"}, + {0x50, nullptr, "CreateSharedMemory32"}, + {0x51, nullptr, "MapTransferMemory32"}, + {0x52, nullptr, "UnmapTransferMemory32"}, + {0x53, nullptr, "CreateInterruptEvent32"}, + {0x54, nullptr, "QueryPhysicalAddress32"}, + {0x55, nullptr, "QueryIoMapping32"}, + {0x56, nullptr, "CreateDeviceAddressSpace32"}, + {0x57, nullptr, "AttachDeviceAddressSpace32"}, + {0x58, nullptr, "DetachDeviceAddressSpace32"}, + {0x59, nullptr, "MapDeviceAddressSpaceByForce32"}, + {0x5a, nullptr, "MapDeviceAddressSpaceAligned32"}, + {0x5b, nullptr, "MapDeviceAddressSpace32"}, + {0x5c, nullptr, "UnmapDeviceAddressSpace32"}, + {0x5d, nullptr, "InvalidateProcessDataCache32"}, + {0x5e, nullptr, "StoreProcessDataCache32"}, {0x5F, SvcWrap32<FlushProcessDataCache32>, "FlushProcessDataCache32"}, - {0x60, nullptr, "Unknown"}, - {0x61, nullptr, "Unknown"}, - {0x62, nullptr, "Unknown"}, - {0x63, nullptr, "Unknown"}, - {0x64, nullptr, "Unknown"}, + {0x60, nullptr, "StoreProcessDataCache32"}, + {0x61, nullptr, "BreakDebugProcess32"}, + {0x62, nullptr, "TerminateDebugProcess32"}, + {0x63, nullptr, "GetDebugEvent32"}, + {0x64, nullptr, "ContinueDebugEvent32"}, {0x65, nullptr, "GetProcessList32"}, - {0x66, nullptr, "Unknown"}, - {0x67, nullptr, "Unknown"}, - {0x68, nullptr, "Unknown"}, - {0x69, nullptr, "Unknown"}, - {0x6A, nullptr, "Unknown"}, - {0x6B, nullptr, "Unknown"}, - {0x6C, nullptr, "Unknown"}, - {0x6D, nullptr, "Unknown"}, - {0x6E, nullptr, "Unknown"}, + {0x66, nullptr, "GetThreadList"}, + {0x67, nullptr, "GetDebugThreadContext32"}, + {0x68, nullptr, "SetDebugThreadContext32"}, + {0x69, nullptr, "QueryDebugProcessMemory32"}, + {0x6A, nullptr, "ReadDebugProcessMemory32"}, + {0x6B, nullptr, "WriteDebugProcessMemory32"}, + {0x6C, nullptr, "SetHardwareBreakPoint32"}, + {0x6D, nullptr, "GetDebugThreadParam32"}, + {0x6E, nullptr, "Unknown6E"}, {0x6f, nullptr, "GetSystemInfo32"}, {0x70, nullptr, "CreatePort32"}, {0x71, nullptr, "ManageNamedPort32"}, {0x72, nullptr, "ConnectToPort32"}, {0x73, nullptr, "SetProcessMemoryPermission32"}, - {0x74, nullptr, "Unknown"}, - {0x75, nullptr, "Unknown"}, - {0x76, nullptr, "Unknown"}, + {0x74, nullptr, "MapProcessMemory32"}, + {0x75, nullptr, "UnmapProcessMemory32"}, + {0x76, nullptr, "QueryProcessMemory32"}, {0x77, nullptr, "MapProcessCodeMemory32"}, {0x78, nullptr, "UnmapProcessCodeMemory32"}, - {0x79, nullptr, "Unknown"}, - {0x7A, nullptr, "Unknown"}, + {0x79, nullptr, "CreateProcess32"}, + {0x7A, nullptr, "StartProcess32"}, {0x7B, nullptr, "TerminateProcess32"}, {0x7C, nullptr, "GetProcessInfo32"}, {0x7D, nullptr, "CreateResourceLimit32"}, @@ -2754,7 +2796,7 @@ static const FunctionDef SVC_Table_32[] = { }; static const FunctionDef SVC_Table_64[] = { - {0x00, nullptr, "Unknown"}, + {0x00, nullptr, "Unknown0"}, {0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"}, {0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"}, {0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"}, @@ -2809,23 +2851,23 @@ static const FunctionDef SVC_Table_64[] = { {0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"}, {0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"}, {0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"}, - {0x37, nullptr, "Unknown"}, - {0x38, nullptr, "Unknown"}, - {0x39, nullptr, "Unknown"}, - {0x3A, nullptr, "Unknown"}, - {0x3B, nullptr, "Unknown"}, + {0x37, nullptr, "GetResourceLimitPeakValue"}, + {0x38, nullptr, "Unknown38"}, + {0x39, nullptr, "CreateIoPool"}, + {0x3A, nullptr, "CreateIoRegion"}, + {0x3B, nullptr, "Unknown3B"}, {0x3C, SvcWrap64<KernelDebug>, "KernelDebug"}, {0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"}, - {0x3E, nullptr, "Unknown"}, - {0x3F, nullptr, "Unknown"}, + {0x3E, nullptr, "Unknown3e"}, + {0x3F, nullptr, "Unknown3f"}, {0x40, nullptr, "CreateSession"}, {0x41, nullptr, "AcceptSession"}, {0x42, nullptr, "ReplyAndReceiveLight"}, {0x43, nullptr, "ReplyAndReceive"}, {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"}, {0x45, SvcWrap64<CreateEvent>, "CreateEvent"}, - {0x46, nullptr, "Unknown"}, - {0x47, nullptr, "Unknown"}, + {0x46, nullptr, "MapIoRegion"}, + {0x47, nullptr, "UnmapIoRegion"}, {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"}, {0x4A, nullptr, "SetUnsafeLimit"}, @@ -2864,7 +2906,7 @@ static const FunctionDef SVC_Table_64[] = { {0x6B, nullptr, "WriteDebugProcessMemory"}, {0x6C, nullptr, "SetHardwareBreakPoint"}, {0x6D, nullptr, "GetDebugThreadParam"}, - {0x6E, nullptr, "Unknown"}, + {0x6E, nullptr, "Unknown6E"}, {0x6F, nullptr, "GetSystemInfo"}, {0x70, nullptr, "CreatePort"}, {0x71, nullptr, "ManageNamedPort"}, @@ -2965,11 +3007,10 @@ static const FunctionDef* GetSVCInfo64(u32 func_num) { } void Call(Core::System& system, u32 immediate) { - system.ExitDynarmicProfile(); auto& kernel = system.Kernel(); kernel.EnterSVCProfile(); - auto* thread = kernel.CurrentScheduler()->GetCurrentThread(); + auto* thread = GetCurrentThreadPointer(kernel); thread->SetIsCallingSvc(); const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate) @@ -2985,13 +3026,6 @@ void Call(Core::System& system, u32 immediate) { } kernel.ExitSVCProfile(); - - if (!thread->IsCallingSvc()) { - auto* host_context = thread->GetHostContext().get(); - host_context->Rewind(); - } - - system.EnterDynarmicProfile(); } } // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index 46e64277e..13f061b83 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/svc_common.h b/src/core/hle/kernel/svc_common.h index 25de6e437..95750c3eb 100644 --- a/src/core/hle/kernel/svc_common.h +++ b/src/core/hle/kernel/svc_common.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h index 53a940723..f27cade33 100644 --- a/src/core/hle/kernel/svc_results.h +++ b/src/core/hle/kernel/svc_results.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,34 +9,34 @@ namespace Kernel { // Confirmed Switch kernel error codes -constexpr ResultCode ResultOutOfSessions{ErrorModule::Kernel, 7}; -constexpr ResultCode ResultInvalidArgument{ErrorModule::Kernel, 14}; -constexpr ResultCode ResultNoSynchronizationObject{ErrorModule::Kernel, 57}; -constexpr ResultCode ResultTerminationRequested{ErrorModule::Kernel, 59}; -constexpr ResultCode ResultInvalidSize{ErrorModule::Kernel, 101}; -constexpr ResultCode ResultInvalidAddress{ErrorModule::Kernel, 102}; -constexpr ResultCode ResultOutOfResource{ErrorModule::Kernel, 103}; -constexpr ResultCode ResultOutOfMemory{ErrorModule::Kernel, 104}; -constexpr ResultCode ResultOutOfHandles{ErrorModule::Kernel, 105}; -constexpr ResultCode ResultInvalidCurrentMemory{ErrorModule::Kernel, 106}; -constexpr ResultCode ResultInvalidNewMemoryPermission{ErrorModule::Kernel, 108}; -constexpr ResultCode ResultInvalidMemoryRegion{ErrorModule::Kernel, 110}; -constexpr ResultCode ResultInvalidPriority{ErrorModule::Kernel, 112}; -constexpr ResultCode ResultInvalidCoreId{ErrorModule::Kernel, 113}; -constexpr ResultCode ResultInvalidHandle{ErrorModule::Kernel, 114}; -constexpr ResultCode ResultInvalidPointer{ErrorModule::Kernel, 115}; -constexpr ResultCode ResultInvalidCombination{ErrorModule::Kernel, 116}; -constexpr ResultCode ResultTimedOut{ErrorModule::Kernel, 117}; -constexpr ResultCode ResultCancelled{ErrorModule::Kernel, 118}; -constexpr ResultCode ResultOutOfRange{ErrorModule::Kernel, 119}; -constexpr ResultCode ResultInvalidEnumValue{ErrorModule::Kernel, 120}; -constexpr ResultCode ResultNotFound{ErrorModule::Kernel, 121}; -constexpr ResultCode ResultBusy{ErrorModule::Kernel, 122}; -constexpr ResultCode ResultSessionClosed{ErrorModule::Kernel, 123}; -constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125}; -constexpr ResultCode ResultReservedUsed{ErrorModule::Kernel, 126}; -constexpr ResultCode ResultPortClosed{ErrorModule::Kernel, 131}; -constexpr ResultCode ResultLimitReached{ErrorModule::Kernel, 132}; -constexpr ResultCode ResultInvalidId{ErrorModule::Kernel, 519}; +constexpr Result ResultOutOfSessions{ErrorModule::Kernel, 7}; +constexpr Result ResultInvalidArgument{ErrorModule::Kernel, 14}; +constexpr Result ResultNoSynchronizationObject{ErrorModule::Kernel, 57}; +constexpr Result ResultTerminationRequested{ErrorModule::Kernel, 59}; +constexpr Result ResultInvalidSize{ErrorModule::Kernel, 101}; +constexpr Result ResultInvalidAddress{ErrorModule::Kernel, 102}; +constexpr Result ResultOutOfResource{ErrorModule::Kernel, 103}; +constexpr Result ResultOutOfMemory{ErrorModule::Kernel, 104}; +constexpr Result ResultOutOfHandles{ErrorModule::Kernel, 105}; +constexpr Result ResultInvalidCurrentMemory{ErrorModule::Kernel, 106}; +constexpr Result ResultInvalidNewMemoryPermission{ErrorModule::Kernel, 108}; +constexpr Result ResultInvalidMemoryRegion{ErrorModule::Kernel, 110}; +constexpr Result ResultInvalidPriority{ErrorModule::Kernel, 112}; +constexpr Result ResultInvalidCoreId{ErrorModule::Kernel, 113}; +constexpr Result ResultInvalidHandle{ErrorModule::Kernel, 114}; +constexpr Result ResultInvalidPointer{ErrorModule::Kernel, 115}; +constexpr Result ResultInvalidCombination{ErrorModule::Kernel, 116}; +constexpr Result ResultTimedOut{ErrorModule::Kernel, 117}; +constexpr Result ResultCancelled{ErrorModule::Kernel, 118}; +constexpr Result ResultOutOfRange{ErrorModule::Kernel, 119}; +constexpr Result ResultInvalidEnumValue{ErrorModule::Kernel, 120}; +constexpr Result ResultNotFound{ErrorModule::Kernel, 121}; +constexpr Result ResultBusy{ErrorModule::Kernel, 122}; +constexpr Result ResultSessionClosed{ErrorModule::Kernel, 123}; +constexpr Result ResultInvalidState{ErrorModule::Kernel, 125}; +constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126}; +constexpr Result ResultPortClosed{ErrorModule::Kernel, 131}; +constexpr Result ResultLimitReached{ErrorModule::Kernel, 132}; +constexpr Result ResultInvalidId{ErrorModule::Kernel, 519}; } // namespace Kernel diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index 365e22e4e..79e15183a 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -96,4 +95,6 @@ constexpr inline s32 IdealCoreNoUpdate = -3; constexpr inline s32 LowestThreadPriority = 63; constexpr inline s32 HighestThreadPriority = 0; +constexpr inline size_t ThreadLocalRegionSize = 0x200; + } // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index a60adfcab..4bc49087e 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -34,24 +33,24 @@ static inline void FuncReturn32(Core::System& system, u32 result) { } //////////////////////////////////////////////////////////////////////////////////////////////////// -// Function wrappers that return type ResultCode +// Function wrappers that return type Result -template <ResultCode func(Core::System&, u64)> +template <Result func(Core::System&, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0)).raw); } -template <ResultCode func(Core::System&, u64, u64)> +template <Result func(Core::System&, u64, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), Param(system, 1)).raw); } -template <ResultCode func(Core::System&, u32)> +template <Result func(Core::System&, u32)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw); } -template <ResultCode func(Core::System&, u32, u32)> +template <Result func(Core::System&, u32, u32)> void SvcWrap64(Core::System& system) { FuncReturn( system, @@ -59,14 +58,14 @@ void SvcWrap64(Core::System& system) { } // Used by SetThreadActivity -template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)> +template <Result func(Core::System&, Handle, Svc::ThreadActivity)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), static_cast<Svc::ThreadActivity>(Param(system, 1))) .raw); } -template <ResultCode func(Core::System&, u32, u64, u64, u64)> +template <Result func(Core::System&, u32, u64, u64, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2), Param(system, 3)) @@ -74,7 +73,7 @@ void SvcWrap64(Core::System& system) { } // Used by MapProcessMemory and UnmapProcessMemory -template <ResultCode func(Core::System&, u64, u32, u64, u64)> +template <Result func(Core::System&, u64, u32, u64, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3)) @@ -82,7 +81,7 @@ void SvcWrap64(Core::System& system) { } // Used by ControlCodeMemory -template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)> +template <Result func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3), @@ -90,7 +89,7 @@ void SvcWrap64(Core::System& system) { .raw); } -template <ResultCode func(Core::System&, u32*)> +template <Result func(Core::System&, u32*)> void SvcWrap64(Core::System& system) { u32 param = 0; const u32 retval = func(system, ¶m).raw; @@ -98,7 +97,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u32*, u32)> +template <Result func(Core::System&, u32*, u32)> void SvcWrap64(Core::System& system) { u32 param_1 = 0; const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1))).raw; @@ -106,7 +105,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u32*, u32*)> +template <Result func(Core::System&, u32*, u32*)> void SvcWrap64(Core::System& system) { u32 param_1 = 0; u32 param_2 = 0; @@ -119,7 +118,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u32*, u64)> +template <Result func(Core::System&, u32*, u64)> void SvcWrap64(Core::System& system) { u32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1)).raw; @@ -127,7 +126,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u32*, u64, u32)> +template <Result func(Core::System&, u32*, u64, u32)> void SvcWrap64(Core::System& system) { u32 param_1 = 0; const u32 retval = @@ -137,7 +136,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u64*, u32)> +template <Result func(Core::System&, u64*, u32)> void SvcWrap64(Core::System& system) { u64 param_1 = 0; const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1))).raw; @@ -146,12 +145,12 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u64, u32)> +template <Result func(Core::System&, u64, u32)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw); } -template <ResultCode func(Core::System&, u64*, u64)> +template <Result func(Core::System&, u64*, u64)> void SvcWrap64(Core::System& system) { u64 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1)).raw; @@ -160,7 +159,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u64*, u32, u32)> +template <Result func(Core::System&, u64*, u32, u32)> void SvcWrap64(Core::System& system) { u64 param_1 = 0; const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1)), @@ -172,7 +171,7 @@ void SvcWrap64(Core::System& system) { } // Used by GetResourceLimitLimitValue. -template <ResultCode func(Core::System&, u64*, Handle, LimitableResource)> +template <Result func(Core::System&, u64*, Handle, LimitableResource)> void SvcWrap64(Core::System& system) { u64 param_1 = 0; const u32 retval = func(system, ¶m_1, static_cast<Handle>(Param(system, 1)), @@ -183,13 +182,13 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u32, u64)> +template <Result func(Core::System&, u32, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw); } // Used by SetResourceLimitLimitValue -template <ResultCode func(Core::System&, Handle, LimitableResource, u64)> +template <Result func(Core::System&, Handle, LimitableResource, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), static_cast<LimitableResource>(Param(system, 1)), Param(system, 2)) @@ -197,7 +196,7 @@ void SvcWrap64(Core::System& system) { } // Used by SetThreadCoreMask -template <ResultCode func(Core::System&, Handle, s32, u64)> +template <Result func(Core::System&, Handle, s32, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1)), Param(system, 2)) @@ -205,44 +204,44 @@ void SvcWrap64(Core::System& system) { } // Used by GetThreadCoreMask -template <ResultCode func(Core::System&, Handle, s32*, u64*)> +template <Result func(Core::System&, Handle, s32*, u64*)> void SvcWrap64(Core::System& system) { s32 param_1 = 0; u64 param_2 = 0; - const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), ¶m_1, ¶m_2); + const Result retval = func(system, static_cast<u32>(Param(system, 2)), ¶m_1, ¶m_2); system.CurrentArmInterface().SetReg(1, param_1); system.CurrentArmInterface().SetReg(2, param_2); FuncReturn(system, retval.raw); } -template <ResultCode func(Core::System&, u64, u64, u32, u32)> +template <Result func(Core::System&, u64, u64, u32, u32)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3))) .raw); } -template <ResultCode func(Core::System&, u64, u64, u32, u64)> +template <Result func(Core::System&, u64, u64, u32, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2)), Param(system, 3)) .raw); } -template <ResultCode func(Core::System&, u32, u64, u32)> +template <Result func(Core::System&, u32, u64, u32)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), static_cast<u32>(Param(system, 2))) .raw); } -template <ResultCode func(Core::System&, u64, u64, u64)> +template <Result func(Core::System&, u64, u64, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw); } -template <ResultCode func(Core::System&, u64, u64, u32)> +template <Result func(Core::System&, u64, u64, u32)> void SvcWrap64(Core::System& system) { FuncReturn( system, @@ -250,7 +249,7 @@ void SvcWrap64(Core::System& system) { } // Used by SetMemoryPermission -template <ResultCode func(Core::System&, u64, u64, Svc::MemoryPermission)> +template <Result func(Core::System&, u64, u64, Svc::MemoryPermission)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), Param(system, 1), static_cast<Svc::MemoryPermission>(Param(system, 2))) @@ -258,14 +257,14 @@ void SvcWrap64(Core::System& system) { } // Used by MapSharedMemory -template <ResultCode func(Core::System&, Handle, u64, u64, Svc::MemoryPermission)> +template <Result func(Core::System&, Handle, u64, u64, Svc::MemoryPermission)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), Param(system, 1), Param(system, 2), static_cast<Svc::MemoryPermission>(Param(system, 3))) .raw); } -template <ResultCode func(Core::System&, u32, u64, u64)> +template <Result func(Core::System&, u32, u64, u64)> void SvcWrap64(Core::System& system) { FuncReturn( system, @@ -273,7 +272,7 @@ void SvcWrap64(Core::System& system) { } // Used by WaitSynchronization -template <ResultCode func(Core::System&, s32*, u64, s32, s64)> +template <Result func(Core::System&, s32*, u64, s32, s64)> void SvcWrap64(Core::System& system) { s32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<s32>(Param(system, 2)), @@ -284,7 +283,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u64, u64, u32, s64)> +template <Result func(Core::System&, u64, u64, u32, s64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) @@ -292,7 +291,7 @@ void SvcWrap64(Core::System& system) { } // Used by GetInfo -template <ResultCode func(Core::System&, u64*, u64, Handle, u64)> +template <Result func(Core::System&, u64*, u64, Handle, u64)> void SvcWrap64(Core::System& system) { u64 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1), @@ -303,7 +302,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)> +template <Result func(Core::System&, u32*, u64, u64, u64, u32, s32)> void SvcWrap64(Core::System& system) { u32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2), Param(system, 3), @@ -315,7 +314,7 @@ void SvcWrap64(Core::System& system) { } // Used by CreateTransferMemory -template <ResultCode func(Core::System&, Handle*, u64, u64, Svc::MemoryPermission)> +template <Result func(Core::System&, Handle*, u64, u64, Svc::MemoryPermission)> void SvcWrap64(Core::System& system) { u32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2), @@ -327,7 +326,7 @@ void SvcWrap64(Core::System& system) { } // Used by CreateCodeMemory -template <ResultCode func(Core::System&, Handle*, u64, u64)> +template <Result func(Core::System&, Handle*, u64, u64)> void SvcWrap64(Core::System& system) { u32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2)).raw; @@ -336,7 +335,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, Handle*, u64, u32, u32)> +template <Result func(Core::System&, Handle*, u64, u32, u32)> void SvcWrap64(Core::System& system) { u32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)), @@ -348,7 +347,7 @@ void SvcWrap64(Core::System& system) { } // Used by WaitForAddress -template <ResultCode func(Core::System&, u64, Svc::ArbitrationType, s32, s64)> +template <Result func(Core::System&, u64, Svc::ArbitrationType, s32, s64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), static_cast<Svc::ArbitrationType>(Param(system, 1)), @@ -357,7 +356,7 @@ void SvcWrap64(Core::System& system) { } // Used by SignalToAddress -template <ResultCode func(Core::System&, u64, Svc::SignalType, s32, s32)> +template <Result func(Core::System&, u64, Svc::SignalType, s32, s32)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), static_cast<Svc::SignalType>(Param(system, 1)), @@ -426,7 +425,7 @@ void SvcWrap64(Core::System& system) { } // Used by QueryMemory32, ArbitrateLock32 -template <ResultCode func(Core::System&, u32, u32, u32)> +template <Result func(Core::System&, u32, u32, u32)> void SvcWrap32(Core::System& system) { FuncReturn32(system, func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw); @@ -457,7 +456,7 @@ void SvcWrap32(Core::System& system) { } // Used by CreateThread32 -template <ResultCode func(Core::System&, Handle*, u32, u32, u32, u32, s32)> +template <Result func(Core::System&, Handle*, u32, u32, u32, u32, s32)> void SvcWrap32(Core::System& system) { Handle param_1 = 0; @@ -470,7 +469,7 @@ void SvcWrap32(Core::System& system) { } // Used by GetInfo32 -template <ResultCode func(Core::System&, u32*, u32*, u32, u32, u32, u32)> +template <Result func(Core::System&, u32*, u32*, u32, u32, u32, u32)> void SvcWrap32(Core::System& system) { u32 param_1 = 0; u32 param_2 = 0; @@ -485,7 +484,7 @@ void SvcWrap32(Core::System& system) { } // Used by GetThreadPriority32, ConnectToNamedPort32 -template <ResultCode func(Core::System&, u32*, u32)> +template <Result func(Core::System&, u32*, u32)> void SvcWrap32(Core::System& system) { u32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param32(system, 1)).raw; @@ -494,7 +493,7 @@ void SvcWrap32(Core::System& system) { } // Used by GetThreadId32 -template <ResultCode func(Core::System&, u32*, u32*, u32)> +template <Result func(Core::System&, u32*, u32*, u32)> void SvcWrap32(Core::System& system) { u32 param_1 = 0; u32 param_2 = 0; @@ -517,7 +516,7 @@ void SvcWrap32(Core::System& system) { } // Used by CreateEvent32 -template <ResultCode func(Core::System&, Handle*, Handle*)> +template <Result func(Core::System&, Handle*, Handle*)> void SvcWrap32(Core::System& system) { Handle param_1 = 0; Handle param_2 = 0; @@ -529,7 +528,7 @@ void SvcWrap32(Core::System& system) { } // Used by GetThreadId32 -template <ResultCode func(Core::System&, Handle, u32*, u32*, u32*)> +template <Result func(Core::System&, Handle, u32*, u32*, u32*)> void SvcWrap32(Core::System& system) { u32 param_1 = 0; u32 param_2 = 0; @@ -543,7 +542,7 @@ void SvcWrap32(Core::System& system) { } // Used by GetThreadCoreMask32 -template <ResultCode func(Core::System&, Handle, s32*, u32*, u32*)> +template <Result func(Core::System&, Handle, s32*, u32*, u32*)> void SvcWrap32(Core::System& system) { s32 param_1 = 0; u32 param_2 = 0; @@ -563,7 +562,7 @@ void SvcWrap32(Core::System& system) { } // Used by SetThreadActivity32 -template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)> +template <Result func(Core::System&, Handle, Svc::ThreadActivity)> void SvcWrap32(Core::System& system) { const u32 retval = func(system, static_cast<Handle>(Param(system, 0)), static_cast<Svc::ThreadActivity>(Param(system, 1))) @@ -572,7 +571,7 @@ void SvcWrap32(Core::System& system) { } // Used by SetThreadPriority32 -template <ResultCode func(Core::System&, Handle, u32)> +template <Result func(Core::System&, Handle, u32)> void SvcWrap32(Core::System& system) { const u32 retval = func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw; @@ -580,7 +579,7 @@ void SvcWrap32(Core::System& system) { } // Used by SetMemoryAttribute32 -template <ResultCode func(Core::System&, Handle, u32, u32, u32)> +template <Result func(Core::System&, Handle, u32, u32, u32)> void SvcWrap32(Core::System& system) { const u32 retval = func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1)), @@ -590,7 +589,7 @@ void SvcWrap32(Core::System& system) { } // Used by MapSharedMemory32 -template <ResultCode func(Core::System&, Handle, u32, u32, Svc::MemoryPermission)> +template <Result func(Core::System&, Handle, u32, u32, Svc::MemoryPermission)> void SvcWrap32(Core::System& system) { const u32 retval = func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1)), static_cast<u32>(Param(system, 2)), @@ -600,7 +599,7 @@ void SvcWrap32(Core::System& system) { } // Used by SetThreadCoreMask32 -template <ResultCode func(Core::System&, Handle, s32, u32, u32)> +template <Result func(Core::System&, Handle, s32, u32, u32)> void SvcWrap32(Core::System& system) { const u32 retval = func(system, static_cast<Handle>(Param(system, 0)), static_cast<s32>(Param(system, 1)), @@ -610,7 +609,7 @@ void SvcWrap32(Core::System& system) { } // Used by WaitProcessWideKeyAtomic32 -template <ResultCode func(Core::System&, u32, u32, Handle, u32, u32)> +template <Result func(Core::System&, u32, u32, Handle, u32, u32)> void SvcWrap32(Core::System& system) { const u32 retval = func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)), @@ -621,7 +620,7 @@ void SvcWrap32(Core::System& system) { } // Used by WaitForAddress32 -template <ResultCode func(Core::System&, u32, Svc::ArbitrationType, s32, u32, u32)> +template <Result func(Core::System&, u32, Svc::ArbitrationType, s32, u32, u32)> void SvcWrap32(Core::System& system) { const u32 retval = func(system, static_cast<u32>(Param(system, 0)), static_cast<Svc::ArbitrationType>(Param(system, 1)), @@ -632,7 +631,7 @@ void SvcWrap32(Core::System& system) { } // Used by SignalToAddress32 -template <ResultCode func(Core::System&, u32, Svc::SignalType, s32, s32)> +template <Result func(Core::System&, u32, Svc::SignalType, s32, s32)> void SvcWrap32(Core::System& system) { const u32 retval = func(system, static_cast<u32>(Param(system, 0)), static_cast<Svc::SignalType>(Param(system, 1)), @@ -642,13 +641,13 @@ void SvcWrap32(Core::System& system) { } // Used by SendSyncRequest32, ArbitrateUnlock32 -template <ResultCode func(Core::System&, u32)> +template <Result func(Core::System&, u32)> void SvcWrap32(Core::System& system) { FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw); } // Used by CreateTransferMemory32 -template <ResultCode func(Core::System&, Handle*, u32, u32, Svc::MemoryPermission)> +template <Result func(Core::System&, Handle*, u32, u32, Svc::MemoryPermission)> void SvcWrap32(Core::System& system) { Handle handle = 0; const u32 retval = func(system, &handle, Param32(system, 1), Param32(system, 2), @@ -659,7 +658,7 @@ void SvcWrap32(Core::System& system) { } // Used by WaitSynchronization32 -template <ResultCode func(Core::System&, u32, u32, s32, u32, s32*)> +template <Result func(Core::System&, u32, u32, s32, u32, s32*)> void SvcWrap32(Core::System& system) { s32 param_1 = 0; const u32 retval = func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2), @@ -669,4 +668,26 @@ void SvcWrap32(Core::System& system) { FuncReturn(system, retval); } +// Used by CreateCodeMemory32 +template <Result func(Core::System&, Handle*, u32, u32)> +void SvcWrap32(Core::System& system) { + Handle handle = 0; + + const u32 retval = func(system, &handle, Param32(system, 1), Param32(system, 2)).raw; + + system.CurrentArmInterface().SetReg(1, handle); + FuncReturn(system, retval); +} + +// Used by ControlCodeMemory32 +template <Result func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)> +void SvcWrap32(Core::System& system) { + const u32 retval = + func(system, Param32(system, 0), Param32(system, 1), Param(system, 2), Param(system, 4), + static_cast<Svc::MemoryPermission>(Param32(system, 6))) + .raw; + + FuncReturn(system, retval); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index aa985d820..5ee72c432 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "core/core.h" @@ -12,19 +11,21 @@ namespace Kernel { TimeManager::TimeManager(Core::System& system_) : system{system_} { - time_manager_event_type = - Core::Timing::CreateEvent("Kernel::TimeManagerCallback", - [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { - KThread* thread = reinterpret_cast<KThread*>(thread_handle); - { - KScopedSchedulerLock sl(system.Kernel()); - thread->OnTimer(); - } - }); + time_manager_event_type = Core::Timing::CreateEvent( + "Kernel::TimeManagerCallback", + [this](std::uintptr_t thread_handle, s64 time, + std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { + KThread* thread = reinterpret_cast<KThread*>(thread_handle); + { + KScopedSchedulerLock sl(system.Kernel()); + thread->OnTimer(); + } + return std::nullopt; + }); } void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; if (nanoseconds > 0) { ASSERT(thread); ASSERT(thread->GetState() != ThreadState::Runnable); @@ -35,7 +36,7 @@ void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { } void TimeManager::UnscheduleTimeEvent(KThread* thread) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; system.CoreTiming().UnscheduleEvent(time_manager_event_type, reinterpret_cast<uintptr_t>(thread)); } diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h index b1fa26e8c..94d16b3b4 100644 --- a/src/core/hle/kernel/time_manager.h +++ b/src/core/hle/kernel/time_manager.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 3807b9aa8..47a1b829b 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -1,6 +1,5 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -112,15 +111,16 @@ enum class ErrorModule : u32 { }; /// Encapsulates a Horizon OS error code, allowing it to be separated into its constituent fields. -union ResultCode { +union Result { u32 raw; BitField<0, 9, ErrorModule> module; BitField<9, 13, u32> description; - constexpr explicit ResultCode(u32 raw_) : raw(raw_) {} + Result() = default; + constexpr explicit Result(u32 raw_) : raw(raw_) {} - constexpr ResultCode(ErrorModule module_, u32 description_) + constexpr Result(ErrorModule module_, u32 description_) : raw(module.FormatValue(module_) | description.FormatValue(description_)) {} [[nodiscard]] constexpr bool IsSuccess() const { @@ -131,19 +131,20 @@ union ResultCode { return !IsSuccess(); } }; +static_assert(std::is_trivial_v<Result>); -[[nodiscard]] constexpr bool operator==(const ResultCode& a, const ResultCode& b) { +[[nodiscard]] constexpr bool operator==(const Result& a, const Result& b) { return a.raw == b.raw; } -[[nodiscard]] constexpr bool operator!=(const ResultCode& a, const ResultCode& b) { +[[nodiscard]] constexpr bool operator!=(const Result& a, const Result& b) { return !operator==(a, b); } // Convenience functions for creating some common kinds of errors: -/// The default success `ResultCode`. -constexpr ResultCode ResultSuccess(0); +/// The default success `Result`. +constexpr Result ResultSuccess(0); /** * Placeholder result code used for unknown error codes. @@ -151,10 +152,52 @@ constexpr ResultCode ResultSuccess(0); * @note This should only be used when a particular error code * is not known yet. */ -constexpr ResultCode ResultUnknown(UINT32_MAX); +constexpr Result ResultUnknown(UINT32_MAX); /** - * This is an optional value type. It holds a `ResultCode` and, if that code is ResultSuccess, it + * A ResultRange defines an inclusive range of error descriptions within an error module. + * This can be used to check whether the description of a given Result falls within the range. + * The conversion function returns a Result with its description set to description_start. + * + * An example of how it could be used: + * \code + * constexpr ResultRange ResultCommonError{ErrorModule::Common, 0, 9999}; + * + * Result Example(int value) { + * const Result result = OtherExample(value); + * + * // This will only evaluate to true if result.module is ErrorModule::Common and + * // result.description is in between 0 and 9999 inclusive. + * if (ResultCommonError.Includes(result)) { + * // This returns Result{ErrorModule::Common, 0}; + * return ResultCommonError; + * } + * + * return ResultSuccess; + * } + * \endcode + */ +class ResultRange { +public: + consteval ResultRange(ErrorModule module, u32 description_start, u32 description_end_) + : code{module, description_start}, description_end{description_end_} {} + + [[nodiscard]] constexpr operator Result() const { + return code; + } + + [[nodiscard]] constexpr bool Includes(Result other) const { + return code.module == other.module && code.description <= other.description && + other.description <= description_end; + } + +private: + Result code; + u32 description_end; +}; + +/** + * This is an optional value type. It holds a `Result` and, if that code is ResultSuccess, it * also holds a result of type `T`. If the code is an error code (not ResultSuccess), then trying * to access the inner value with operator* is undefined behavior and will assert with Unwrap(). * Users of this class must be cognizant to check the status of the ResultVal with operator bool(), @@ -165,7 +208,7 @@ constexpr ResultCode ResultUnknown(UINT32_MAX); * ResultVal<int> Frobnicate(float strength) { * if (strength < 0.f || strength > 1.0f) { * // Can't frobnicate too weakly or too strongly - * return ResultCode{ErrorModule::Common, 1}; + * return Result{ErrorModule::Common, 1}; * } else { * // Frobnicated! Give caller a cookie * return 42; @@ -188,7 +231,9 @@ class ResultVal { public: constexpr ResultVal() : expected{} {} - constexpr ResultVal(ResultCode code) : expected{Common::Unexpected(code)} {} + constexpr ResultVal(Result code) : expected{Common::Unexpected(code)} {} + + constexpr ResultVal(ResultRange range) : expected{Common::Unexpected(range)} {} template <typename U> constexpr ResultVal(U&& val) : expected{std::forward<U>(val)} {} @@ -208,7 +253,7 @@ public: return expected.has_value(); } - [[nodiscard]] constexpr ResultCode Code() const { + [[nodiscard]] constexpr Result Code() const { return expected.has_value() ? ResultSuccess : expected.error(); } @@ -275,8 +320,8 @@ public: } private: - // TODO: Replace this with std::expected once it is standardized in the STL. - Common::Expected<T, ResultCode> expected; + // TODO (Morph): Replace this with C++23 std::expected. + Common::Expected<T, Result> expected; }; /** @@ -293,7 +338,7 @@ private: target = std::move(*CONCAT2(check_result_L, __LINE__)) /** - * Analogous to CASCADE_RESULT, but for a bare ResultCode. The code will be propagated if + * Analogous to CASCADE_RESULT, but for a bare Result. The code will be propagated if * non-success, or discarded otherwise. */ #define CASCADE_CODE(source) \ diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index e34ef5a78..bb838e285 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> @@ -16,7 +15,6 @@ #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/kernel.h" #include "core/hle/service/acc/acc.h" #include "core/hle/service/acc/acc_aa.h" #include "core/hle/service/acc/acc_su.h" @@ -30,11 +28,11 @@ namespace Service::Account { -constexpr ResultCode ERR_INVALID_USER_ID{ErrorModule::Account, 20}; -constexpr ResultCode ERR_INVALID_APPLICATION_ID{ErrorModule::Account, 22}; -constexpr ResultCode ERR_INVALID_BUFFER{ErrorModule::Account, 30}; -constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 31}; -constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100}; +constexpr Result ERR_INVALID_USER_ID{ErrorModule::Account, 20}; +constexpr Result ERR_INVALID_APPLICATION_ID{ErrorModule::Account, 22}; +constexpr Result ERR_INVALID_BUFFER{ErrorModule::Account, 30}; +constexpr Result ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 31}; +constexpr Result ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100}; // Thumbnails are hard coded to be at least this size constexpr std::size_t THUMBNAIL_SIZE = 0x24000; @@ -292,7 +290,7 @@ protected: void Get(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString()); ProfileBase profile_base{}; - ProfileData data{}; + UserData data{}; if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { ctx.WriteBuffer(data); IPC::ResponseBuilder rb{ctx, 16}; @@ -375,18 +373,18 @@ protected: reinterpret_cast<const char*>(base.username.data()), base.username.size()), base.timestamp, base.user_uuid.RawString()); - if (user_data.size() < sizeof(ProfileData)) { - LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); + if (user_data.size() < sizeof(UserData)) { + LOG_ERROR(Service_ACC, "UserData buffer too small!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_INVALID_BUFFER); return; } - ProfileData data; - std::memcpy(&data, user_data.data(), sizeof(ProfileData)); + UserData data; + std::memcpy(&data, user_data.data(), sizeof(UserData)); if (!profile_manager.SetProfileBaseAndData(user_id, base, data)) { - LOG_ERROR(Service_ACC, "Failed to update profile data and base!"); + LOG_ERROR(Service_ACC, "Failed to update user data and base!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_FAILED_SAVE_DATA); return; @@ -408,15 +406,15 @@ protected: reinterpret_cast<const char*>(base.username.data()), base.username.size()), base.timestamp, base.user_uuid.RawString()); - if (user_data.size() < sizeof(ProfileData)) { - LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); + if (user_data.size() < sizeof(UserData)) { + LOG_ERROR(Service_ACC, "UserData buffer too small!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_INVALID_BUFFER); return; } - ProfileData data; - std::memcpy(&data, user_data.data(), sizeof(ProfileData)); + UserData data; + std::memcpy(&data, user_data.data(), sizeof(UserData)); Common::FS::IOFile image(GetImagePath(user_id), Common::FS::FileAccessMode::Write, Common::FS::FileType::BinaryFile); @@ -507,7 +505,7 @@ protected: void Cancel() override {} - ResultCode GetResult() const override { + Result GetResult() const override { return ResultSuccess; } }; @@ -536,7 +534,7 @@ public: private: void CheckAvailability(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_DEBUG(Service_ACC, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(false); // TODO: Check when this is supposed to return true and when not @@ -749,7 +747,7 @@ void Module::Interface::InitializeApplicationInfoRestricted(Kernel::HLERequestCo rb.Push(InitializeApplicationInfoBase()); } -ResultCode Module::Interface::InitializeApplicationInfoBase() { +Result Module::Interface::InitializeApplicationInfoBase() { if (application_info) { LOG_ERROR(Service_ACC, "Application already initialized"); return ERR_ACCOUNTINFO_ALREADY_INITIALIZED; diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index f7e9bc4f8..1621e7c0a 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -42,7 +41,7 @@ public: void StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx); private: - ResultCode InitializeApplicationInfoBase(); + Result InitializeApplicationInfoBase(); void StoreSaveDataThumbnail(Kernel::HLERequestContext& ctx, const Common::UUID& uuid, const u64 tid); diff --git a/src/core/hle/service/acc/acc_aa.cpp b/src/core/hle/service/acc/acc_aa.cpp index e498fb64d..90ed0f519 100644 --- a/src/core/hle/service/acc/acc_aa.cpp +++ b/src/core/hle/service/acc/acc_aa.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/acc/acc_aa.h" diff --git a/src/core/hle/service/acc/acc_aa.h b/src/core/hle/service/acc/acc_aa.h index d1be20ff3..623daeaef 100644 --- a/src/core/hle/service/acc/acc_aa.h +++ b/src/core/hle/service/acc/acc_aa.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index f4034d591..b6bfd6155 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/acc/acc_su.h" diff --git a/src/core/hle/service/acc/acc_su.h b/src/core/hle/service/acc/acc_su.h index 132a126b4..8daef38b8 100644 --- a/src/core/hle/service/acc/acc_su.h +++ b/src/core/hle/service/acc/acc_su.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index df77c58f0..65023b8c2 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/acc/acc_u0.h" diff --git a/src/core/hle/service/acc/acc_u0.h b/src/core/hle/service/acc/acc_u0.h index 4c2600b67..35cd4b492 100644 --- a/src/core/hle/service/acc/acc_u0.h +++ b/src/core/hle/service/acc/acc_u0.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index 991921984..92f704c2f 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/acc/acc_u1.h" diff --git a/src/core/hle/service/acc/acc_u1.h b/src/core/hle/service/acc/acc_u1.h index 2d478324a..e711d3925 100644 --- a/src/core/hle/service/acc/acc_u1.h +++ b/src/core/hle/service/acc/acc_u1.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/acc/async_context.cpp b/src/core/hle/service/acc/async_context.cpp index a49dfdec7..c85b2e43a 100644 --- a/src/core/hle/service/acc/async_context.cpp +++ b/src/core/hle/service/acc/async_context.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/hle/ipc_helpers.h" diff --git a/src/core/hle/service/acc/async_context.h b/src/core/hle/service/acc/async_context.h index cc3a0a9fe..26332d241 100644 --- a/src/core/hle/service/acc/async_context.h +++ b/src/core/hle/service/acc/async_context.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -27,7 +26,7 @@ public: protected: virtual bool IsComplete() const = 0; virtual void Cancel() = 0; - virtual ResultCode GetResult() const = 0; + virtual Result GetResult() const = 0; void MarkComplete(); diff --git a/src/core/hle/service/acc/errors.h b/src/core/hle/service/acc/errors.h index 1f0577239..e9c16b951 100644 --- a/src/core/hle/service/acc/errors.h +++ b/src/core/hle/service/acc/errors.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,7 +7,7 @@ namespace Service::Account { -constexpr ResultCode ERR_ACCOUNTINFO_BAD_APPLICATION{ErrorModule::Account, 22}; -constexpr ResultCode ERR_ACCOUNTINFO_ALREADY_INITIALIZED{ErrorModule::Account, 41}; +constexpr Result ERR_ACCOUNTINFO_BAD_APPLICATION{ErrorModule::Account, 22}; +constexpr Result ERR_ACCOUNTINFO_ALREADY_INITIALIZED{ErrorModule::Account, 41}; } // namespace Service::Account diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index fba847142..a58da4d5f 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include <random> @@ -23,7 +22,7 @@ struct UserRaw { UUID uuid2{}; u64 timestamp{}; ProfileUsername username{}; - ProfileData extra_data{}; + UserData extra_data{}; }; static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size."); @@ -34,9 +33,9 @@ struct ProfileDataRaw { static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size."); // TODO(ogniK): Get actual error codes -constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, u32(-1)); -constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, u32(-2)); -constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); +constexpr Result ERROR_TOO_MANY_USERS(ErrorModule::Account, u32(-1)); +constexpr Result ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, u32(-2)); +constexpr Result ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "system/save/8000000000000010/su/avators"; @@ -88,7 +87,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) { } /// Helper function to register a user to the system -ResultCode ProfileManager::AddUser(const ProfileInfo& user) { +Result ProfileManager::AddUser(const ProfileInfo& user) { if (!AddToProfiles(user)) { return ERROR_TOO_MANY_USERS; } @@ -97,7 +96,7 @@ ResultCode ProfileManager::AddUser(const ProfileInfo& user) { /// Create a new user on the system. If the uuid of the user already exists, the user is not /// created. -ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& username) { +Result ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& username) { if (user_count == MAX_USERS) { return ERROR_TOO_MANY_USERS; } @@ -124,7 +123,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern /// Creates a new user on the system. This function allows a much simpler method of registration /// specifically by allowing an std::string for the username. This is required specifically since /// we're loading a string straight from the config -ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) { +Result ProfileManager::CreateNewUser(UUID uuid, const std::string& username) { ProfileUsername username_output{}; if (username.size() > username_output.size()) { @@ -264,7 +263,7 @@ UUID ProfileManager::GetLastOpenedUser() const { /// Return the users profile base and the unknown arbitary data. bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, - ProfileData& data) const { + UserData& data) const { if (GetProfileBase(index, profile)) { data = profiles[*index].data; return true; @@ -273,15 +272,14 @@ bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, Pro } /// Return the users profile base and the unknown arbitary data. -bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, - ProfileData& data) const { +bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, UserData& data) const { const auto idx = GetUserIndex(uuid); return GetProfileBaseAndData(idx, profile, data); } /// Return the users profile base and the unknown arbitary data. bool ProfileManager::GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, - ProfileData& data) const { + UserData& data) const { return GetProfileBaseAndData(user.user_uuid, profile, data); } @@ -319,7 +317,7 @@ bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) { } bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new, - const ProfileData& data_new) { + const UserData& data_new) { const auto index = GetUserIndex(uuid); if (index.has_value() && SetProfileBase(uuid, profile_new)) { profiles[*index].data = data_new; diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 17347f7ef..135f7d0d5 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -1,12 +1,12 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <array> #include <optional> +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" #include "common/uuid.h" @@ -22,7 +22,7 @@ using UserIDArray = std::array<Common::UUID, MAX_USERS>; /// Contains extra data related to a user. /// TODO: RE this structure -struct ProfileData { +struct UserData { INSERT_PADDING_WORDS_NOINIT(1); u32 icon_id; u8 bg_color_id; @@ -30,7 +30,7 @@ struct ProfileData { INSERT_PADDING_BYTES_NOINIT(0x10); INSERT_PADDING_BYTES_NOINIT(0x60); }; -static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect size"); +static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); /// This holds general information about a users profile. This is where we store all the information /// based on a specific user @@ -38,7 +38,7 @@ struct ProfileInfo { Common::UUID user_uuid{}; ProfileUsername username{}; u64 creation_time{}; - ProfileData data{}; // TODO(ognik): Work out what this is + UserData data{}; // TODO(ognik): Work out what this is bool is_open{}; }; @@ -64,9 +64,9 @@ public: ProfileManager(); ~ProfileManager(); - ResultCode AddUser(const ProfileInfo& user); - ResultCode CreateNewUser(Common::UUID uuid, const ProfileUsername& username); - ResultCode CreateNewUser(Common::UUID uuid, const std::string& username); + Result AddUser(const ProfileInfo& user); + Result CreateNewUser(Common::UUID uuid, const ProfileUsername& username); + Result CreateNewUser(Common::UUID uuid, const std::string& username); std::optional<Common::UUID> GetUser(std::size_t index) const; std::optional<std::size_t> GetUserIndex(const Common::UUID& uuid) const; std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; @@ -74,10 +74,9 @@ public: bool GetProfileBase(Common::UUID uuid, ProfileBase& profile) const; bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, - ProfileData& data) const; - bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, ProfileData& data) const; - bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, - ProfileData& data) const; + UserData& data) const; + bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, UserData& data) const; + bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, UserData& data) const; std::size_t GetUserCount() const; std::size_t GetOpenUserCount() const; bool UserExists(Common::UUID uuid) const; @@ -93,7 +92,7 @@ public: bool RemoveUser(Common::UUID uuid); bool SetProfileBase(Common::UUID uuid, const ProfileBase& profile_new); bool SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new, - const ProfileData& data_new); + const UserData& data_new); private: void ParseUserSaveFile(); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 773dc9f29..6fb7e198e 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1,12 +1,10 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> #include <cinttypes> #include <cstring> -#include "audio_core/audio_renderer.h" #include "common/settings.h" #include "core/core.h" #include "core/file_sys/control_metadata.h" @@ -41,9 +39,9 @@ namespace Service::AM { -constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 2}; -constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 3}; -constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 503}; +constexpr Result ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 2}; +constexpr Result ERR_NO_MESSAGES{ErrorModule::AM, 3}; +constexpr Result ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 503}; enum class LaunchParameterKind : u32 { ApplicationSpecific = 1, @@ -239,6 +237,7 @@ IDebugFunctions::IDebugFunctions(Core::System& system_) {130, nullptr, "FriendInvitationSetApplicationParameter"}, {131, nullptr, "FriendInvitationClearApplicationParameter"}, {132, nullptr, "FriendInvitationPushApplicationParameter"}, + {140, nullptr, "RestrictPowerOperationForSecureLaunchModeForDebug"}, {900, nullptr, "GetGrcProcessLaunchedSystemEvent"}, }; // clang-format on @@ -286,7 +285,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"}, {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"}, {64, nullptr, "SetInputDetectionSourceSet"}, - {65, nullptr, "ReportUserIsActive"}, + {65, &ISelfController::ReportUserIsActive, "ReportUserIsActive"}, {66, nullptr, "GetCurrentIlluminance"}, {67, nullptr, "IsIlluminanceAvailable"}, {68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"}, @@ -366,7 +365,7 @@ void ISelfController::LeaveFatalSection(Kernel::HLERequestContext& ctx) { // Entry and exit of fatal sections must be balanced. if (num_fatal_sections_entered == 0) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode{ErrorModule::AM, 512}); + rb.Push(Result{ErrorModule::AM, 512}); return; } @@ -518,6 +517,13 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c rb.Push<u32>(idle_time_detection_extension); } +void ISelfController::ReportUserIsActive(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void ISelfController::SetAutoSleepDisabled(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; is_auto_sleep_disabled = rp.Pop<bool>(); @@ -618,7 +624,7 @@ void AppletMessageQueue::PushMessage(AppletMessage msg) { AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() { if (messages.empty()) { on_new_message->GetWritableEvent().Clear(); - return AppletMessage::NoMessage; + return AppletMessage::None; } auto msg = messages.front(); messages.pop(); @@ -633,7 +639,11 @@ std::size_t AppletMessageQueue::GetMessageCount() const { } void AppletMessageQueue::RequestExit() { - PushMessage(AppletMessage::ExitRequested); + PushMessage(AppletMessage::Exit); +} + +void AppletMessageQueue::RequestResume() { + PushMessage(AppletMessage::Resume); } void AppletMessageQueue::FocusStateChanged() { @@ -687,7 +697,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"}, {67, nullptr, "CancelCpuBoostMode"}, {68, nullptr, "GetBuiltInDisplayType"}, - {80, nullptr, "PerformSystemButtonPressingIfInFocus"}, + {80, &ICommonStateGetter::PerformSystemButtonPressingIfInFocus, "PerformSystemButtonPressingIfInFocus"}, {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, {91, nullptr, "GetCurrentPerformanceConfiguration"}, {100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"}, @@ -732,7 +742,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { const auto message = msg_queue->PopMessage(); IPC::ResponseBuilder rb{ctx, 3}; - if (message == AppletMessageQueue::AppletMessage::NoMessage) { + if (message == AppletMessageQueue::AppletMessage::None) { LOG_ERROR(Service_AM, "Message queue is empty"); rb.Push(ERR_NO_MESSAGES); rb.PushEnum<AppletMessageQueue::AppletMessage>(message); @@ -744,7 +754,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { } void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_DEBUG(Service_AM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); @@ -827,6 +837,16 @@ void ICommonStateGetter::SetCpuBoostMode(Kernel::HLERequestContext& ctx) { apm_sys->SetCpuBoostMode(ctx); } +void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto system_button{rp.PopEnum<SystemButtonType>()}; + + LOG_WARNING(Service_AM, "(STUBBED) called, system_button={}", system_button); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled( Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); @@ -980,7 +1000,7 @@ private: LOG_DEBUG(Service_AM, "called"); IPC::RequestParser rp{ctx}; - applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>()); + applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>().lock()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -1007,7 +1027,7 @@ private: LOG_DEBUG(Service_AM, "called"); IPC::RequestParser rp{ctx}; - applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>()); + applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>().lock()); ASSERT(applet->IsInitialized()); applet->ExecuteInteractive(); @@ -1301,6 +1321,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"}, {34, nullptr, "SelectApplicationLicense"}, {35, nullptr, "GetDeviceSaveDataSizeMax"}, + {36, nullptr, "GetLimitedApplicationLicense"}, + {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"}, {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, {60, nullptr, "SetMediaPlaybackStateForApplication"}, @@ -1337,7 +1359,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) {200, nullptr, "GetLastApplicationExitReason"}, {500, nullptr, "StartContinuousRecordingFlushForDebug"}, {1000, nullptr, "CreateMovieMaker"}, - {1001, nullptr, "PrepareForJit"}, + {1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"}, }; // clang-format on @@ -1787,6 +1809,13 @@ void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERe rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent()); } +void IApplicationFunctions::PrepareForJit(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, Core::System& system) { auto message_queue = std::make_shared<AppletMessageQueue>(system); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 2a578aea5..bb75c6281 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -22,6 +21,7 @@ class NVFlinger; namespace Service::AM { +// This is nn::settings::Language enum SystemLanguage { Japanese = 0, English = 1, // en-US @@ -41,16 +41,44 @@ enum SystemLanguage { // 4.0.0+ SimplifiedChinese = 15, TraditionalChinese = 16, + // 10.1.0+ + BrazilianPortuguese = 17, }; class AppletMessageQueue { public: + // This is nn::am::AppletMessage enum class AppletMessage : u32 { - NoMessage = 0, - ExitRequested = 4, + None = 0, + ChangeIntoForeground = 1, + ChangeIntoBackground = 2, + Exit = 4, + ApplicationExited = 6, FocusStateChanged = 15, + Resume = 16, + DetectShortPressingHomeButton = 20, + DetectLongPressingHomeButton = 21, + DetectShortPressingPowerButton = 22, + DetectMiddlePressingPowerButton = 23, + DetectLongPressingPowerButton = 24, + RequestToPrepareSleep = 25, + FinishedSleepSequence = 26, + SleepRequiredByHighTemperature = 27, + SleepRequiredByLowBattery = 28, + AutoPowerDown = 29, OperationModeChanged = 30, PerformanceModeChanged = 31, + DetectReceivingCecSystemStandby = 32, + SdCardRemoved = 33, + LaunchApplicationRequested = 50, + RequestToDisplay = 51, + ShowApplicationLogo = 55, + HideApplicationLogo = 56, + ForceHideApplicationLogo = 57, + FloatingApplicationDetected = 60, + DetectShortPressingCaptureButton = 90, + AlbumScreenShotTaken = 92, + AlbumRecordingSaved = 93, }; explicit AppletMessageQueue(Core::System& system); @@ -62,6 +90,7 @@ public: AppletMessage PopMessage(); std::size_t GetMessageCount() const; void RequestExit(); + void RequestResume(); void FocusStateChanged(); void OperationModeChanged(); @@ -146,6 +175,7 @@ private: void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); + void ReportUserIsActive(Kernel::HLERequestContext& ctx); void SetAutoSleepDisabled(Kernel::HLERequestContext& ctx); void IsAutoSleepDisabled(Kernel::HLERequestContext& ctx); void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); @@ -179,16 +209,31 @@ public: ~ICommonStateGetter() override; private: + // This is nn::oe::FocusState enum class FocusState : u8 { InFocus = 1, NotInFocus = 2, + Background = 3, }; + // This is nn::oe::OperationMode enum class OperationMode : u8 { Handheld = 0, Docked = 1, }; + // This is nn::am::service::SystemButtonType + enum class SystemButtonType { + None, + HomeButtonShortPressing, + HomeButtonLongPressing, + PowerButtonShortPressing, + PowerButtonLongPressing, + ShutdownSystem, + CaptureButtonShortPressing, + CaptureButtonLongPressing, + }; + void GetEventHandle(Kernel::HLERequestContext& ctx); void ReceiveMessage(Kernel::HLERequestContext& ctx); void GetCurrentFocusState(Kernel::HLERequestContext& ctx); @@ -203,6 +248,7 @@ private: void EndVrModeEx(Kernel::HLERequestContext& ctx); void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); void SetCpuBoostMode(Kernel::HLERequestContext& ctx); + void PerformSystemButtonPressingIfInFocus(Kernel::HLERequestContext& ctx); void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(Kernel::HLERequestContext& ctx); std::shared_ptr<AppletMessageQueue> msg_queue; @@ -304,6 +350,7 @@ private: void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx); void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx); void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx); + void PrepareForJit(Kernel::HLERequestContext& ctx); KernelHelpers::ServiceContext service_context; diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index 0ec4fd4ca..d7719da35 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/core.h" diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h index f89f65649..2147976a6 100644 --- a/src/core/hle/service/am/applet_ae.h +++ b/src/core/hle/service/am/applet_ae.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp index b8859f4e6..00fc4202c 100644 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h index 64b874ead..8fea249f1 100644 --- a/src/core/hle/service/am/applet_oe.h +++ b/src/core/hle/service/am/applet_oe.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp index d073f2210..b418031de 100644 --- a/src/core/hle/service/am/applets/applet_controller.cpp +++ b/src/core/hle/service/am/applets/applet_controller.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstring> @@ -21,9 +20,9 @@ namespace Service::AM::Applets { // This error code (0x183ACA) is thrown when the applet fails to initialize. -[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101}; +[[maybe_unused]] constexpr Result ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101}; // This error code (0x183CCA) is thrown when the u32 result in ControllerSupportResultInfo is 2. -[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102}; +[[maybe_unused]] constexpr Result ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102}; static Core::Frontend::ControllerParameters ConvertToFrontendParameters( ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text, @@ -174,12 +173,12 @@ bool Controller::TransactionComplete() const { return complete; } -ResultCode Controller::GetStatus() const { +Result Controller::GetStatus() const { return status; } void Controller::ExecuteInteractive() { - UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); + ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet."); } void Controller::Execute() { diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h index 1a832505e..1f9adec65 100644 --- a/src/core/hle/service/am/applets/applet_controller.h +++ b/src/core/hle/service/am/applets/applet_controller.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -127,7 +126,7 @@ public: void Initialize() override; bool TransactionComplete() const override; - ResultCode GetStatus() const override; + Result GetStatus() const override; void ExecuteInteractive() override; void Execute() override; @@ -144,7 +143,7 @@ private: ControllerUpdateFirmwareArg controller_update_arg; ControllerKeyRemappingArg controller_key_remapping_arg; bool complete{false}; - ResultCode status{ResultSuccess}; + Result status{ResultSuccess}; bool is_single_mode{false}; std::vector<u8> out_data; }; diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp index a06c2b872..fcf34bf7e 100644 --- a/src/core/hle/service/am/applets/applet_error.cpp +++ b/src/core/hle/service/am/applets/applet_error.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include <cstring> @@ -26,15 +25,15 @@ struct ErrorCode { }; } - static constexpr ErrorCode FromResultCode(ResultCode result) { + static constexpr ErrorCode FromResult(Result result) { return { .error_category{2000 + static_cast<u32>(result.module.Value())}, .error_number{result.description.Value()}, }; } - constexpr ResultCode ToResultCode() const { - return ResultCode{static_cast<ErrorModule>(error_category - 2000), error_number}; + constexpr Result ToResult() const { + return Result{static_cast<ErrorModule>(error_category - 2000), error_number}; } }; static_assert(sizeof(ErrorCode) == 0x8, "ErrorCode has incorrect size."); @@ -98,8 +97,8 @@ void CopyArgumentData(const std::vector<u8>& data, T& variable) { std::memcpy(&variable, data.data(), sizeof(T)); } -ResultCode Decode64BitError(u64 error) { - return ErrorCode::FromU64(error).ToResultCode(); +Result Decode64BitError(u64 error) { + return ErrorCode::FromU64(error).ToResult(); } } // Anonymous namespace @@ -128,16 +127,16 @@ void Error::Initialize() { if (args->error.use_64bit_error_code) { error_code = Decode64BitError(args->error.error_code_64); } else { - error_code = ResultCode(args->error.error_code_32); + error_code = Result(args->error.error_code_32); } break; case ErrorAppletMode::ShowSystemError: CopyArgumentData(data, args->system_error); - error_code = ResultCode(Decode64BitError(args->system_error.error_code_64)); + error_code = Result(Decode64BitError(args->system_error.error_code_64)); break; case ErrorAppletMode::ShowApplicationError: CopyArgumentData(data, args->application_error); - error_code = ResultCode(args->application_error.error_code); + error_code = Result(args->application_error.error_code); break; case ErrorAppletMode::ShowErrorRecord: CopyArgumentData(data, args->error_record); @@ -152,12 +151,12 @@ bool Error::TransactionComplete() const { return complete; } -ResultCode Error::GetStatus() const { +Result Error::GetStatus() const { return ResultSuccess; } void Error::ExecuteInteractive() { - UNREACHABLE_MSG("Unexpected interactive applet data!"); + ASSERT_MSG(false, "Unexpected interactive applet data!"); } void Error::Execute() { diff --git a/src/core/hle/service/am/applets/applet_error.h b/src/core/hle/service/am/applets/applet_error.h index 8aa9046a5..d78d6f1d1 100644 --- a/src/core/hle/service/am/applets/applet_error.h +++ b/src/core/hle/service/am/applets/applet_error.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -32,7 +31,7 @@ public: void Initialize() override; bool TransactionComplete() const override; - ResultCode GetStatus() const override; + Result GetStatus() const override; void ExecuteInteractive() override; void Execute() override; @@ -42,7 +41,7 @@ private: union ErrorArguments; const Core::Frontend::ErrorApplet& frontend; - ResultCode error_code = ResultSuccess; + Result error_code = ResultSuccess; ErrorAppletMode mode = ErrorAppletMode::ShowError; std::unique_ptr<ErrorArguments> args; diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp index 2c6e9d83c..c34ef08b3 100644 --- a/src/core/hle/service/am/applets/applet_general_backend.cpp +++ b/src/core/hle/service/am/applets/applet_general_backend.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/hex_util.h" @@ -14,7 +13,7 @@ namespace Service::AM::Applets { -constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221}; +constexpr Result ERROR_INVALID_PIN{ErrorModule::PCTL, 221}; static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) { std::shared_ptr<IStorage> storage = broker.PopNormalDataToApplet(); @@ -72,12 +71,12 @@ bool Auth::TransactionComplete() const { return complete; } -ResultCode Auth::GetStatus() const { +Result Auth::GetStatus() const { return successful ? ResultSuccess : ERROR_INVALID_PIN; } void Auth::ExecuteInteractive() { - UNREACHABLE_MSG("Unexpected interactive applet data."); + ASSERT_MSG(false, "Unexpected interactive applet data."); } void Auth::Execute() { @@ -137,7 +136,7 @@ void Auth::AuthFinished(bool is_successful) { successful = is_successful; struct Return { - ResultCode result_code; + Result result_code; }; static_assert(sizeof(Return) == 0x4, "Return (AuthApplet) has incorrect size."); @@ -171,12 +170,12 @@ bool PhotoViewer::TransactionComplete() const { return complete; } -ResultCode PhotoViewer::GetStatus() const { +Result PhotoViewer::GetStatus() const { return ResultSuccess; } void PhotoViewer::ExecuteInteractive() { - UNREACHABLE_MSG("Unexpected interactive applet data."); + ASSERT_MSG(false, "Unexpected interactive applet data."); } void PhotoViewer::Execute() { @@ -224,7 +223,7 @@ bool StubApplet::TransactionComplete() const { return true; } -ResultCode StubApplet::GetStatus() const { +Result StubApplet::GetStatus() const { LOG_WARNING(Service_AM, "called (STUBBED)"); return ResultSuccess; } diff --git a/src/core/hle/service/am/applets/applet_general_backend.h b/src/core/hle/service/am/applets/applet_general_backend.h index 7496ded88..a9f2535a2 100644 --- a/src/core/hle/service/am/applets/applet_general_backend.h +++ b/src/core/hle/service/am/applets/applet_general_backend.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -26,7 +25,7 @@ public: void Initialize() override; bool TransactionComplete() const override; - ResultCode GetStatus() const override; + Result GetStatus() const override; void ExecuteInteractive() override; void Execute() override; @@ -57,7 +56,7 @@ public: void Initialize() override; bool TransactionComplete() const override; - ResultCode GetStatus() const override; + Result GetStatus() const override; void ExecuteInteractive() override; void Execute() override; @@ -78,7 +77,7 @@ public: void Initialize() override; bool TransactionComplete() const override; - ResultCode GetStatus() const override; + Result GetStatus() const override; void ExecuteInteractive() override; void Execute() override; diff --git a/src/core/hle/service/am/applets/applet_mii_edit.cpp b/src/core/hle/service/am/applets/applet_mii_edit.cpp new file mode 100644 index 000000000..ae80ef506 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_mii_edit.cpp @@ -0,0 +1,138 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/frontend/applets/mii_edit.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/applet_mii_edit.h" +#include "core/hle/service/mii/mii_manager.h" + +namespace Service::AM::Applets { + +MiiEdit::MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::MiiEditApplet& frontend_) + : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} + +MiiEdit::~MiiEdit() = default; + +void MiiEdit::Initialize() { + // Note: MiiEdit is not initialized with common arguments. + // Instead, it is initialized by an AppletInput storage with size 0x100 bytes. + // Do NOT call Applet::Initialize() here. + + const auto storage = broker.PopNormalDataToApplet(); + ASSERT(storage != nullptr); + + const auto applet_input_data = storage->GetData(); + ASSERT(applet_input_data.size() >= sizeof(MiiEditAppletInputCommon)); + + std::memcpy(&applet_input_common, applet_input_data.data(), sizeof(MiiEditAppletInputCommon)); + + LOG_INFO(Service_AM, + "Initializing MiiEdit Applet with MiiEditAppletVersion={} and MiiEditAppletMode={}", + applet_input_common.version, applet_input_common.applet_mode); + + switch (applet_input_common.version) { + case MiiEditAppletVersion::Version3: + ASSERT(applet_input_data.size() == + sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV3)); + std::memcpy(&applet_input_v3, applet_input_data.data() + sizeof(MiiEditAppletInputCommon), + sizeof(MiiEditAppletInputV3)); + break; + case MiiEditAppletVersion::Version4: + ASSERT(applet_input_data.size() == + sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4)); + std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon), + sizeof(MiiEditAppletInputV4)); + break; + default: + UNIMPLEMENTED_MSG("Unknown MiiEditAppletVersion={} with size={}", + applet_input_common.version, applet_input_data.size()); + ASSERT(applet_input_data.size() >= + sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4)); + std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon), + sizeof(MiiEditAppletInputV4)); + break; + } +} + +bool MiiEdit::TransactionComplete() const { + return is_complete; +} + +Result MiiEdit::GetStatus() const { + return ResultSuccess; +} + +void MiiEdit::ExecuteInteractive() { + ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet."); +} + +void MiiEdit::Execute() { + if (is_complete) { + return; + } + + // This is a default stub for each of the MiiEdit applet modes. + switch (applet_input_common.applet_mode) { + case MiiEditAppletMode::ShowMiiEdit: + case MiiEditAppletMode::AppendMii: + case MiiEditAppletMode::AppendMiiImage: + case MiiEditAppletMode::UpdateMiiImage: + MiiEditOutput(MiiEditResult::Success, 0); + break; + case MiiEditAppletMode::CreateMii: + case MiiEditAppletMode::EditMii: { + Service::Mii::MiiManager mii_manager; + + const MiiEditCharInfo char_info{ + .mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii + ? applet_input_v4.char_info.mii_info + : mii_manager.BuildDefault(0)}, + }; + + MiiEditOutputForCharInfoEditing(MiiEditResult::Success, char_info); + break; + } + default: + UNIMPLEMENTED_MSG("Unknown MiiEditAppletMode={}", applet_input_common.applet_mode); + + MiiEditOutput(MiiEditResult::Success, 0); + break; + } +} + +void MiiEdit::MiiEditOutput(MiiEditResult result, s32 index) { + const MiiEditAppletOutput applet_output{ + .result{result}, + .index{index}, + }; + + std::vector<u8> out_data(sizeof(MiiEditAppletOutput)); + std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutput)); + + is_complete = true; + + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); + broker.SignalStateChanged(); +} + +void MiiEdit::MiiEditOutputForCharInfoEditing(MiiEditResult result, + const MiiEditCharInfo& char_info) { + const MiiEditAppletOutputForCharInfoEditing applet_output{ + .result{result}, + .char_info{char_info}, + }; + + std::vector<u8> out_data(sizeof(MiiEditAppletOutputForCharInfoEditing)); + std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutputForCharInfoEditing)); + + is_complete = true; + + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); + broker.SignalStateChanged(); +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_mii_edit.h b/src/core/hle/service/am/applets/applet_mii_edit.h new file mode 100644 index 000000000..d18dd3cf5 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_mii_edit.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" +#include "core/hle/service/am/applets/applet_mii_edit_types.h" +#include "core/hle/service/am/applets/applets.h" + +namespace Core { +class System; +} // namespace Core + +namespace Service::AM::Applets { + +class MiiEdit final : public Applet { +public: + explicit MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::MiiEditApplet& frontend_); + ~MiiEdit() override; + + void Initialize() override; + + bool TransactionComplete() const override; + Result GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + void MiiEditOutput(MiiEditResult result, s32 index); + + void MiiEditOutputForCharInfoEditing(MiiEditResult result, const MiiEditCharInfo& char_info); + +private: + const Core::Frontend::MiiEditApplet& frontend; + Core::System& system; + + MiiEditAppletInputCommon applet_input_common{}; + MiiEditAppletInputV3 applet_input_v3{}; + MiiEditAppletInputV4 applet_input_v4{}; + + bool is_complete{false}; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_mii_edit_types.h b/src/core/hle/service/am/applets/applet_mii_edit_types.h new file mode 100644 index 000000000..4705d019f --- /dev/null +++ b/src/core/hle/service/am/applets/applet_mii_edit_types.h @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/service/mii/types.h" + +namespace Service::AM::Applets { + +enum class MiiEditAppletVersion : s32 { + Version3 = 0x3, // 1.0.0 - 10.1.1 + Version4 = 0x4, // 10.2.0+ +}; + +// This is nn::mii::AppletMode +enum class MiiEditAppletMode : u32 { + ShowMiiEdit = 0, + AppendMii = 1, + AppendMiiImage = 2, + UpdateMiiImage = 3, + CreateMii = 4, + EditMii = 5, +}; + +enum class MiiEditResult : u32 { + Success, + Cancel, +}; + +struct MiiEditCharInfo { + Service::Mii::CharInfo mii_info{}; +}; +static_assert(sizeof(MiiEditCharInfo) == 0x58, "MiiEditCharInfo has incorrect size."); + +struct MiiEditAppletInputCommon { + MiiEditAppletVersion version{}; + MiiEditAppletMode applet_mode{}; +}; +static_assert(sizeof(MiiEditAppletInputCommon) == 0x8, + "MiiEditAppletInputCommon has incorrect size."); + +struct MiiEditAppletInputV3 { + u32 special_mii_key_code{}; + std::array<Common::UUID, 8> valid_uuids{}; + Common::UUID used_uuid{}; + INSERT_PADDING_BYTES(0x64); +}; +static_assert(sizeof(MiiEditAppletInputV3) == 0x100 - sizeof(MiiEditAppletInputCommon), + "MiiEditAppletInputV3 has incorrect size."); + +struct MiiEditAppletInputV4 { + u32 special_mii_key_code{}; + MiiEditCharInfo char_info{}; + INSERT_PADDING_BYTES(0x28); + Common::UUID used_uuid{}; + INSERT_PADDING_BYTES(0x64); +}; +static_assert(sizeof(MiiEditAppletInputV4) == 0x100 - sizeof(MiiEditAppletInputCommon), + "MiiEditAppletInputV4 has incorrect size."); + +// This is nn::mii::AppletOutput +struct MiiEditAppletOutput { + MiiEditResult result{}; + s32 index{}; + INSERT_PADDING_BYTES(0x18); +}; +static_assert(sizeof(MiiEditAppletOutput) == 0x20, "MiiEditAppletOutput has incorrect size."); + +// This is nn::mii::AppletOutputForCharInfoEditing +struct MiiEditAppletOutputForCharInfoEditing { + MiiEditResult result{}; + MiiEditCharInfo char_info{}; + INSERT_PADDING_BYTES(0x24); +}; +static_assert(sizeof(MiiEditAppletOutputForCharInfoEditing) == 0x80, + "MiiEditAppletOutputForCharInfoEditing has incorrect size."); + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_profile_select.cpp b/src/core/hle/service/am/applets/applet_profile_select.cpp index 82500e121..c738db028 100644 --- a/src/core/hle/service/am/applets/applet_profile_select.cpp +++ b/src/core/hle/service/am/applets/applet_profile_select.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> @@ -13,7 +12,7 @@ namespace Service::AM::Applets { -constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; +constexpr Result ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; ProfileSelect::ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_, const Core::Frontend::ProfileSelectApplet& frontend_) @@ -40,12 +39,12 @@ bool ProfileSelect::TransactionComplete() const { return complete; } -ResultCode ProfileSelect::GetStatus() const { +Result ProfileSelect::GetStatus() const { return status; } void ProfileSelect::ExecuteInteractive() { - UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); + ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet."); } void ProfileSelect::Execute() { diff --git a/src/core/hle/service/am/applets/applet_profile_select.h b/src/core/hle/service/am/applets/applet_profile_select.h index 852e1e0c0..b77f1d205 100644 --- a/src/core/hle/service/am/applets/applet_profile_select.h +++ b/src/core/hle/service/am/applets/applet_profile_select.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -40,7 +39,7 @@ public: void Initialize() override; bool TransactionComplete() const override; - ResultCode GetStatus() const override; + Result GetStatus() const override; void ExecuteInteractive() override; void Execute() override; @@ -51,7 +50,7 @@ private: UserSelectionConfig config; bool complete = false; - ResultCode status = ResultSuccess; + Result status = ResultSuccess; std::vector<u8> final_data; Core::System& system; }; diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.cpp b/src/core/hle/service/am/applets/applet_software_keyboard.cpp index f38f53f69..c18236045 100644 --- a/src/core/hle/service/am/applets/applet_software_keyboard.cpp +++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/string_util.h" #include "core/core.h" @@ -72,7 +71,7 @@ void SoftwareKeyboard::Initialize() { InitializeBackground(applet_mode); break; default: - UNREACHABLE_MSG("Invalid LibraryAppletMode={}", applet_mode); + ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode); break; } } @@ -81,7 +80,7 @@ bool SoftwareKeyboard::TransactionComplete() const { return complete; } -ResultCode SoftwareKeyboard::GetStatus() const { +Result SoftwareKeyboard::GetStatus() const { return status; } @@ -226,7 +225,7 @@ void SoftwareKeyboard::InitializeForeground() { ASSERT(work_buffer_storage != nullptr); if (swkbd_config_common.initial_string_length == 0) { - InitializeFrontendKeyboard(); + InitializeFrontendNormalKeyboard(); return; } @@ -243,7 +242,7 @@ void SoftwareKeyboard::InitializeForeground() { LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text)); - InitializeFrontendKeyboard(); + InitializeFrontendNormalKeyboard(); } void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) { @@ -480,129 +479,179 @@ void SoftwareKeyboard::ChangeState(SwkbdState state) { ReplyDefault(); } -void SoftwareKeyboard::InitializeFrontendKeyboard() { - if (is_background) { - const auto& appear_arg = swkbd_calc_arg.appear_arg; - - std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - appear_arg.ok_text.data(), appear_arg.ok_text.size()); - - const u32 max_text_length = - appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH - ? appear_arg.max_text_length - : DEFAULT_MAX_TEXT_LENGTH; - - const u32 min_text_length = - appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; - - const s32 initial_cursor_position = - current_cursor_position > 0 ? current_cursor_position : 0; - - const auto text_draw_type = - max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; - - Core::Frontend::KeyboardInitializeParameters initialize_parameters{ - .ok_text{std::move(ok_text)}, - .header_text{}, - .sub_text{}, - .guide_text{}, - .initial_text{current_text}, - .max_text_length{max_text_length}, - .min_text_length{min_text_length}, - .initial_cursor_position{initial_cursor_position}, - .type{appear_arg.type}, - .password_mode{SwkbdPasswordMode::Disabled}, - .text_draw_type{text_draw_type}, - .key_disable_flags{appear_arg.key_disable_flags}, - .use_blur_background{false}, - .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, - .enable_return_button{appear_arg.enable_return_button}, - .disable_cancel_button{appear_arg.disable_cancel_button}, - }; - - frontend.InitializeKeyboard( - true, std::move(initialize_parameters), {}, - [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) { - SubmitTextInline(reply_type, submitted_text, cursor_position); - }); - } else { - std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size()); - - std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size()); - - std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size()); - - std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size()); - - const u32 max_text_length = - swkbd_config_common.max_text_length > 0 && - swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH - ? swkbd_config_common.max_text_length - : DEFAULT_MAX_TEXT_LENGTH; - - const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length - ? swkbd_config_common.min_text_length - : 0; - - const s32 initial_cursor_position = [this] { - switch (swkbd_config_common.initial_cursor_position) { - case SwkbdInitialCursorPosition::Start: - default: - return 0; - case SwkbdInitialCursorPosition::End: - return static_cast<s32>(initial_text.size()); - } - }(); - - const auto text_draw_type = [this, max_text_length] { - switch (swkbd_config_common.text_draw_type) { - case SwkbdTextDrawType::Line: - default: - return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; - case SwkbdTextDrawType::Box: - case SwkbdTextDrawType::DownloadCode: - return swkbd_config_common.text_draw_type; - } - }(); - - const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box - ? swkbd_config_common.enable_return_button - : false; - - const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227 - ? swkbd_config_new.disable_cancel_button - : false; - - Core::Frontend::KeyboardInitializeParameters initialize_parameters{ - .ok_text{std::move(ok_text)}, - .header_text{std::move(header_text)}, - .sub_text{std::move(sub_text)}, - .guide_text{std::move(guide_text)}, - .initial_text{initial_text}, - .max_text_length{max_text_length}, - .min_text_length{min_text_length}, - .initial_cursor_position{initial_cursor_position}, - .type{swkbd_config_common.type}, - .password_mode{swkbd_config_common.password_mode}, - .text_draw_type{text_draw_type}, - .key_disable_flags{swkbd_config_common.key_disable_flags}, - .use_blur_background{swkbd_config_common.use_blur_background}, - .enable_backspace_button{true}, - .enable_return_button{enable_return_button}, - .disable_cancel_button{disable_cancel_button}, - }; - - frontend.InitializeKeyboard( - false, std::move(initialize_parameters), - [this](SwkbdResult result, std::u16string submitted_text, bool confirmed) { - SubmitTextNormal(result, submitted_text, confirmed); - }, - {}); - } +void SoftwareKeyboard::InitializeFrontendNormalKeyboard() { + std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size()); + + std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size()); + + std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size()); + + std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size()); + + const u32 max_text_length = + swkbd_config_common.max_text_length > 0 && + swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? swkbd_config_common.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length + ? swkbd_config_common.min_text_length + : 0; + + const s32 initial_cursor_position = [this] { + switch (swkbd_config_common.initial_cursor_position) { + case SwkbdInitialCursorPosition::Start: + default: + return 0; + case SwkbdInitialCursorPosition::End: + return static_cast<s32>(initial_text.size()); + } + }(); + + const auto text_draw_type = [this, max_text_length] { + switch (swkbd_config_common.text_draw_type) { + case SwkbdTextDrawType::Line: + default: + return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; + case SwkbdTextDrawType::Box: + case SwkbdTextDrawType::DownloadCode: + return swkbd_config_common.text_draw_type; + } + }(); + + const auto enable_return_button = + text_draw_type == SwkbdTextDrawType::Box ? swkbd_config_common.enable_return_button : false; + + const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227 + ? swkbd_config_new.disable_cancel_button + : false; + + Core::Frontend::KeyboardInitializeParameters initialize_parameters{ + .ok_text{std::move(ok_text)}, + .header_text{std::move(header_text)}, + .sub_text{std::move(sub_text)}, + .guide_text{std::move(guide_text)}, + .initial_text{initial_text}, + .left_optional_symbol_key{swkbd_config_common.left_optional_symbol_key}, + .right_optional_symbol_key{swkbd_config_common.right_optional_symbol_key}, + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .initial_cursor_position{initial_cursor_position}, + .type{swkbd_config_common.type}, + .password_mode{swkbd_config_common.password_mode}, + .text_draw_type{text_draw_type}, + .key_disable_flags{swkbd_config_common.key_disable_flags}, + .use_blur_background{swkbd_config_common.use_blur_background}, + .enable_backspace_button{true}, + .enable_return_button{enable_return_button}, + .disable_cancel_button{disable_cancel_button}, + }; + + frontend.InitializeKeyboard( + false, std::move(initialize_parameters), + [this](SwkbdResult result, std::u16string submitted_text, bool confirmed) { + SubmitTextNormal(result, submitted_text, confirmed); + }, + {}); +} + +void SoftwareKeyboard::InitializeFrontendInlineKeyboard( + Core::Frontend::KeyboardInitializeParameters initialize_parameters) { + frontend.InitializeKeyboard( + true, std::move(initialize_parameters), {}, + [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) { + SubmitTextInline(reply_type, submitted_text, cursor_position); + }); +} + +void SoftwareKeyboard::InitializeFrontendInlineKeyboardOld() { + const auto& appear_arg = swkbd_calc_arg_old.appear_arg; + + std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + appear_arg.ok_text.data(), appear_arg.ok_text.size()); + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0; + + const auto text_draw_type = + max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; + + Core::Frontend::KeyboardInitializeParameters initialize_parameters{ + .ok_text{std::move(ok_text)}, + .header_text{}, + .sub_text{}, + .guide_text{}, + .initial_text{current_text}, + .left_optional_symbol_key{appear_arg.left_optional_symbol_key}, + .right_optional_symbol_key{appear_arg.right_optional_symbol_key}, + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .initial_cursor_position{initial_cursor_position}, + .type{appear_arg.type}, + .password_mode{SwkbdPasswordMode::Disabled}, + .text_draw_type{text_draw_type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .use_blur_background{false}, + .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + InitializeFrontendInlineKeyboard(std::move(initialize_parameters)); +} + +void SoftwareKeyboard::InitializeFrontendInlineKeyboardNew() { + const auto& appear_arg = swkbd_calc_arg_new.appear_arg; + + std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + appear_arg.ok_text.data(), appear_arg.ok_text.size()); + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0; + + const auto text_draw_type = + max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; + + Core::Frontend::KeyboardInitializeParameters initialize_parameters{ + .ok_text{std::move(ok_text)}, + .header_text{}, + .sub_text{}, + .guide_text{}, + .initial_text{current_text}, + .left_optional_symbol_key{appear_arg.left_optional_symbol_key}, + .right_optional_symbol_key{appear_arg.right_optional_symbol_key}, + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .initial_cursor_position{initial_cursor_position}, + .type{appear_arg.type}, + .password_mode{SwkbdPasswordMode::Disabled}, + .text_draw_type{text_draw_type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .use_blur_background{false}, + .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + InitializeFrontendInlineKeyboard(std::move(initialize_parameters)); } void SoftwareKeyboard::ShowNormalKeyboard() { @@ -614,14 +663,21 @@ void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_resul frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message)); } -void SoftwareKeyboard::ShowInlineKeyboard() { +void SoftwareKeyboard::ShowInlineKeyboard( + Core::Frontend::InlineAppearParameters appear_parameters) { + frontend.ShowInlineKeyboard(std::move(appear_parameters)); + + ChangeState(SwkbdState::InitializedIsShown); +} + +void SoftwareKeyboard::ShowInlineKeyboardOld() { if (swkbd_state != SwkbdState::InitializedIsHidden) { return; } ChangeState(SwkbdState::InitializedIsAppearing); - const auto& appear_arg = swkbd_calc_arg.appear_arg; + const auto& appear_arg = swkbd_calc_arg_old.appear_arg; const u32 max_text_length = appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH @@ -634,21 +690,54 @@ void SoftwareKeyboard::ShowInlineKeyboard() { Core::Frontend::InlineAppearParameters appear_parameters{ .max_text_length{max_text_length}, .min_text_length{min_text_length}, - .key_top_scale_x{swkbd_calc_arg.key_top_scale_x}, - .key_top_scale_y{swkbd_calc_arg.key_top_scale_y}, - .key_top_translate_x{swkbd_calc_arg.key_top_translate_x}, - .key_top_translate_y{swkbd_calc_arg.key_top_translate_y}, + .key_top_scale_x{swkbd_calc_arg_old.key_top_scale_x}, + .key_top_scale_y{swkbd_calc_arg_old.key_top_scale_y}, + .key_top_translate_x{swkbd_calc_arg_old.key_top_translate_x}, + .key_top_translate_y{swkbd_calc_arg_old.key_top_translate_y}, .type{appear_arg.type}, .key_disable_flags{appear_arg.key_disable_flags}, - .key_top_as_floating{swkbd_calc_arg.key_top_as_floating}, - .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, + .key_top_as_floating{swkbd_calc_arg_old.key_top_as_floating}, + .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button}, .enable_return_button{appear_arg.enable_return_button}, .disable_cancel_button{appear_arg.disable_cancel_button}, }; - frontend.ShowInlineKeyboard(std::move(appear_parameters)); + ShowInlineKeyboard(std::move(appear_parameters)); +} - ChangeState(SwkbdState::InitializedIsShown); +void SoftwareKeyboard::ShowInlineKeyboardNew() { + if (swkbd_state != SwkbdState::InitializedIsHidden) { + return; + } + + ChangeState(SwkbdState::InitializedIsAppearing); + + const auto& appear_arg = swkbd_calc_arg_new.appear_arg; + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + Core::Frontend::InlineAppearParameters appear_parameters{ + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .key_top_scale_x{swkbd_calc_arg_new.key_top_scale_x}, + .key_top_scale_y{swkbd_calc_arg_new.key_top_scale_y}, + .key_top_translate_x{swkbd_calc_arg_new.key_top_translate_x}, + .key_top_translate_y{swkbd_calc_arg_new.key_top_translate_y}, + .type{appear_arg.type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .key_top_as_floating{swkbd_calc_arg_new.key_top_as_floating}, + .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + ShowInlineKeyboard(std::move(appear_parameters)); } void SoftwareKeyboard::HideInlineKeyboard() { @@ -693,6 +782,8 @@ void SoftwareKeyboard::RequestFinalize(const std::vector<u8>& request_data) { void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) { LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented."); + + ReplyReleasedUserWordInfo(); } void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) { @@ -702,53 +793,135 @@ void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_dat void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) { LOG_DEBUG(Service_AM, "Processing Request: Calc"); - ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg)); + ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon)); + + std::memcpy(&swkbd_calc_arg_common, request_data.data() + sizeof(SwkbdRequestCommand), + sizeof(SwkbdCalcArgCommon)); + + switch (swkbd_calc_arg_common.calc_arg_size) { + case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld): + ASSERT(request_data.size() == + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld)); + std::memcpy(&swkbd_calc_arg_old, + request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon), + sizeof(SwkbdCalcArgOld)); + RequestCalcOld(); + break; + case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew): + ASSERT(request_data.size() == + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew)); + std::memcpy(&swkbd_calc_arg_new, + request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon), + sizeof(SwkbdCalcArgNew)); + RequestCalcNew(); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdCalcArg size={}", swkbd_calc_arg_common.calc_arg_size); + ASSERT(request_data.size() >= + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew)); + std::memcpy(&swkbd_calc_arg_new, + request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon), + sizeof(SwkbdCalcArgNew)); + RequestCalcNew(); + break; + } +} + +void SoftwareKeyboard::RequestCalcOld() { + if (swkbd_calc_arg_common.flags.set_input_text) { + current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_calc_arg_old.input_text.data(), swkbd_calc_arg_old.input_text.size()); + } + + if (swkbd_calc_arg_common.flags.set_cursor_position) { + current_cursor_position = swkbd_calc_arg_old.cursor_position; + } + + if (swkbd_calc_arg_common.flags.set_utf8_mode) { + inline_use_utf8 = swkbd_calc_arg_old.utf8_mode; + } - std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand), - sizeof(SwkbdCalcArg)); + if (swkbd_state <= SwkbdState::InitializedIsHidden && + swkbd_calc_arg_common.flags.unset_customize_dic) { + ReplyUnsetCustomizeDic(); + } + + if (swkbd_state <= SwkbdState::InitializedIsHidden && + swkbd_calc_arg_common.flags.unset_user_word_info) { + ReplyReleasedUserWordInfo(); + } + + if (swkbd_state == SwkbdState::NotInitialized && + swkbd_calc_arg_common.flags.set_initialize_arg) { + InitializeFrontendInlineKeyboardOld(); + + ChangeState(SwkbdState::InitializedIsHidden); + + ReplyFinishedInitialize(); + } + + if (!swkbd_calc_arg_common.flags.set_initialize_arg && + (swkbd_calc_arg_common.flags.set_input_text || + swkbd_calc_arg_common.flags.set_cursor_position)) { + InlineTextChanged(); + } + + if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) { + ShowInlineKeyboardOld(); + return; + } + + if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) { + HideInlineKeyboard(); + return; + } +} - if (swkbd_calc_arg.flags.set_input_text) { +void SoftwareKeyboard::RequestCalcNew() { + if (swkbd_calc_arg_common.flags.set_input_text) { current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size()); + swkbd_calc_arg_new.input_text.data(), swkbd_calc_arg_new.input_text.size()); } - if (swkbd_calc_arg.flags.set_cursor_position) { - current_cursor_position = swkbd_calc_arg.cursor_position; + if (swkbd_calc_arg_common.flags.set_cursor_position) { + current_cursor_position = swkbd_calc_arg_new.cursor_position; } - if (swkbd_calc_arg.flags.set_utf8_mode) { - inline_use_utf8 = swkbd_calc_arg.utf8_mode; + if (swkbd_calc_arg_common.flags.set_utf8_mode) { + inline_use_utf8 = swkbd_calc_arg_new.utf8_mode; } if (swkbd_state <= SwkbdState::InitializedIsHidden && - swkbd_calc_arg.flags.unset_customize_dic) { + swkbd_calc_arg_common.flags.unset_customize_dic) { ReplyUnsetCustomizeDic(); } if (swkbd_state <= SwkbdState::InitializedIsHidden && - swkbd_calc_arg.flags.unset_user_word_info) { + swkbd_calc_arg_common.flags.unset_user_word_info) { ReplyReleasedUserWordInfo(); } - if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) { - InitializeFrontendKeyboard(); + if (swkbd_state == SwkbdState::NotInitialized && + swkbd_calc_arg_common.flags.set_initialize_arg) { + InitializeFrontendInlineKeyboardNew(); ChangeState(SwkbdState::InitializedIsHidden); ReplyFinishedInitialize(); } - if (!swkbd_calc_arg.flags.set_initialize_arg && - (swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) { + if (!swkbd_calc_arg_common.flags.set_initialize_arg && + (swkbd_calc_arg_common.flags.set_input_text || + swkbd_calc_arg_common.flags.set_cursor_position)) { InlineTextChanged(); } - if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) { - ShowInlineKeyboard(); + if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) { + ShowInlineKeyboardNew(); return; } - if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) { + if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) { HideInlineKeyboard(); return; } diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.h b/src/core/hle/service/am/applets/applet_software_keyboard.h index a0fddd965..b01b31c98 100644 --- a/src/core/hle/service/am/applets/applet_software_keyboard.h +++ b/src/core/hle/service/am/applets/applet_software_keyboard.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -13,6 +12,11 @@ namespace Core { class System; } +namespace Core::Frontend { +struct KeyboardInitializeParameters; +struct InlineAppearParameters; +} // namespace Core::Frontend + namespace Service::AM::Applets { class SoftwareKeyboard final : public Applet { @@ -24,7 +28,7 @@ public: void Initialize() override; bool TransactionComplete() const override; - ResultCode GetStatus() const override; + Result GetStatus() const override; void ExecuteInteractive() override; void Execute() override; @@ -78,13 +82,22 @@ private: void ChangeState(SwkbdState state); /** - * Signals the frontend to initialize the software keyboard with common parameters. - * This initializes either the normal software keyboard or the inline software keyboard - * depending on the state of is_background. + * Signals the frontend to initialize the normal software keyboard with common parameters. * Note that this does not cause the keyboard to appear. - * Use the respective Show*Keyboard() functions to cause the respective keyboards to appear. + * Use the ShowNormalKeyboard() functions to cause the keyboard to appear. */ - void InitializeFrontendKeyboard(); + void InitializeFrontendNormalKeyboard(); + + /** + * Signals the frontend to initialize the inline software keyboard with common parameters. + * Note that this does not cause the keyboard to appear. + * Use the ShowInlineKeyboard() to cause the keyboard to appear. + */ + void InitializeFrontendInlineKeyboard( + Core::Frontend::KeyboardInitializeParameters initialize_parameters); + + void InitializeFrontendInlineKeyboardOld(); + void InitializeFrontendInlineKeyboardNew(); /// Signals the frontend to show the normal software keyboard. void ShowNormalKeyboard(); @@ -94,7 +107,10 @@ private: std::u16string text_check_message); /// Signals the frontend to show the inline software keyboard. - void ShowInlineKeyboard(); + void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters); + + void ShowInlineKeyboardOld(); + void ShowInlineKeyboardNew(); /// Signals the frontend to hide the inline software keyboard. void HideInlineKeyboard(); @@ -111,6 +127,8 @@ private: void RequestSetUserWordInfo(const std::vector<u8>& request_data); void RequestSetCustomizeDic(const std::vector<u8>& request_data); void RequestCalc(const std::vector<u8>& request_data); + void RequestCalcOld(); + void RequestCalcNew(); void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data); void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data); void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data); @@ -149,7 +167,9 @@ private: SwkbdState swkbd_state{SwkbdState::NotInitialized}; SwkbdInitializeArg swkbd_initialize_arg; - SwkbdCalcArg swkbd_calc_arg; + SwkbdCalcArgCommon swkbd_calc_arg_common; + SwkbdCalcArgOld swkbd_calc_arg_old; + SwkbdCalcArgNew swkbd_calc_arg_new; bool use_changed_string_v2{false}; bool use_moved_cursor_v2{false}; bool inline_use_utf8{false}; @@ -160,7 +180,7 @@ private: bool is_background{false}; bool complete{false}; - ResultCode status{ResultSuccess}; + Result status{ResultSuccess}; }; } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_software_keyboard_types.h b/src/core/hle/service/am/applets/applet_software_keyboard_types.h index 21aa8e800..1f696900e 100644 --- a/src/core/hle/service/am/applets/applet_software_keyboard_types.h +++ b/src/core/hle/service/am/applets/applet_software_keyboard_types.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,6 +9,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "common/uuid.h" namespace Service::AM::Applets { @@ -216,7 +216,7 @@ struct SwkbdInitializeArg { }; static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size."); -struct SwkbdAppearArg { +struct SwkbdAppearArgOld { SwkbdType type{}; std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{}; char16_t left_optional_symbol_key{}; @@ -229,19 +229,76 @@ struct SwkbdAppearArg { bool enable_return_button{}; INSERT_PADDING_BYTES(3); u32 flags{}; - INSERT_PADDING_WORDS(6); + bool is_use_save_data{}; + INSERT_PADDING_BYTES(7); + Common::UUID user_id{}; }; -static_assert(sizeof(SwkbdAppearArg) == 0x48, "SwkbdAppearArg has incorrect size."); +static_assert(sizeof(SwkbdAppearArgOld) == 0x48, "SwkbdAppearArg has incorrect size."); -struct SwkbdCalcArg { +struct SwkbdAppearArgNew { + SwkbdType type{}; + std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{}; + char16_t left_optional_symbol_key{}; + char16_t right_optional_symbol_key{}; + bool use_prediction{}; + bool disable_cancel_button{}; + SwkbdKeyDisableFlags key_disable_flags{}; + u32 max_text_length{}; + u32 min_text_length{}; + bool enable_return_button{}; + INSERT_PADDING_BYTES(3); + u32 flags{}; + bool is_use_save_data{}; + INSERT_PADDING_BYTES(7); + Common::UUID user_id{}; + u64 start_sampling_number{}; + INSERT_PADDING_WORDS(8); +}; +static_assert(sizeof(SwkbdAppearArgNew) == 0x70, "SwkbdAppearArg has incorrect size."); + +struct SwkbdCalcArgCommon { u32 unknown{}; u16 calc_arg_size{}; INSERT_PADDING_BYTES(2); SwkbdCalcArgFlags flags{}; SwkbdInitializeArg initialize_arg{}; +}; +static_assert(sizeof(SwkbdCalcArgCommon) == 0x18, "SwkbdCalcArgCommon has incorrect size."); + +struct SwkbdCalcArgOld { + f32 volume{}; + s32 cursor_position{}; + SwkbdAppearArgOld appear_arg{}; + std::array<char16_t, 0x1FA> input_text{}; + bool utf8_mode{}; + INSERT_PADDING_BYTES(1); + bool enable_backspace_button{}; + INSERT_PADDING_BYTES(3); + bool key_top_as_floating{}; + bool footer_scalable{}; + bool alpha_enabled_in_input_mode{}; + u8 input_mode_fade_type{}; + bool disable_touch{}; + bool disable_hardware_keyboard{}; + INSERT_PADDING_BYTES(8); + f32 key_top_scale_x{}; + f32 key_top_scale_y{}; + f32 key_top_translate_x{}; + f32 key_top_translate_y{}; + f32 key_top_bg_alpha{}; + f32 footer_bg_alpha{}; + f32 balloon_scale{}; + INSERT_PADDING_WORDS(4); + u8 se_group{}; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(SwkbdCalcArgOld) == 0x4A0 - sizeof(SwkbdCalcArgCommon), + "SwkbdCalcArgOld has incorrect size."); + +struct SwkbdCalcArgNew { + SwkbdAppearArgNew appear_arg{}; f32 volume{}; s32 cursor_position{}; - SwkbdAppearArg appear_arg{}; std::array<char16_t, 0x1FA> input_text{}; bool utf8_mode{}; INSERT_PADDING_BYTES(1); @@ -264,8 +321,10 @@ struct SwkbdCalcArg { INSERT_PADDING_WORDS(4); u8 se_group{}; INSERT_PADDING_BYTES(3); + INSERT_PADDING_WORDS(8); }; -static_assert(sizeof(SwkbdCalcArg) == 0x4A0, "SwkbdCalcArg has incorrect size."); +static_assert(sizeof(SwkbdCalcArgNew) == 0x4E8 - sizeof(SwkbdCalcArgCommon), + "SwkbdCalcArgNew has incorrect size."); struct SwkbdChangedStringArg { u32 text_length{}; diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp index bb5cb61be..14aa6f69e 100644 --- a/src/core/hle/service/am/applets/applet_web_browser.cpp +++ b/src/core/hle/service/am/applets/applet_web_browser.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/fs/file.h" @@ -22,7 +21,7 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/applet_web_browser.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/ns/pl_u.h" +#include "core/hle/service/ns/iplatform_service_manager.h" #include "core/loader/loader.h" namespace Service::AM::Applets { @@ -280,7 +279,7 @@ void WebBrowser::Initialize() { InitializeLobby(); break; default: - UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); + ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind); break; } } @@ -289,7 +288,7 @@ bool WebBrowser::TransactionComplete() const { return complete; } -ResultCode WebBrowser::GetStatus() const { +Result WebBrowser::GetStatus() const { return status; } @@ -321,7 +320,7 @@ void WebBrowser::Execute() { ExecuteLobby(); break; default: - UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); + ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind); WebBrowserExit(WebExitReason::EndButtonPressed); break; } @@ -446,6 +445,14 @@ void WebBrowser::ExecuteLogin() { } void WebBrowser::ExecuteOffline() { + // TODO (Morph): This is a hack for WebSession foreground web applets such as those used by + // Super Mario 3D All-Stars. + // TODO (Morph): Implement WebSession. + if (applet_mode == LibraryAppletMode::AllForegroundInitiallyHidden) { + LOG_WARNING(Service_AM, "WebSession is not implemented"); + return; + } + const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document)); if (!Common::FS::Exists(main_url)) { diff --git a/src/core/hle/service/am/applets/applet_web_browser.h b/src/core/hle/service/am/applets/applet_web_browser.h index b3364ee06..fd727fac8 100644 --- a/src/core/hle/service/am/applets/applet_web_browser.h +++ b/src/core/hle/service/am/applets/applet_web_browser.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -33,7 +32,7 @@ public: void Initialize() override; bool TransactionComplete() const override; - ResultCode GetStatus() const override; + Result GetStatus() const override; void ExecuteInteractive() override; void Execute() override; @@ -67,7 +66,7 @@ private: const Core::Frontend::WebBrowserApplet& frontend; bool complete{false}; - ResultCode status{ResultSuccess}; + Result status{ResultSuccess}; WebAppletVersion web_applet_version{}; WebArgHeader web_arg_header{}; diff --git a/src/core/hle/service/am/applets/applet_web_browser_types.h b/src/core/hle/service/am/applets/applet_web_browser_types.h index 419c2bf79..c522c5c1a 100644 --- a/src/core/hle/service/am/applets/applet_web_browser_types.h +++ b/src/core/hle/service/am/applets/applet_web_browser_types.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 134ac1ee2..b5b8e4cad 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> @@ -9,6 +8,7 @@ #include "core/frontend/applets/controller.h" #include "core/frontend/applets/error.h" #include "core/frontend/applets/general_frontend.h" +#include "core/frontend/applets/mii_edit.h" #include "core/frontend/applets/profile_select.h" #include "core/frontend/applets/software_keyboard.h" #include "core/frontend/applets/web_browser.h" @@ -19,6 +19,7 @@ #include "core/hle/service/am/applets/applet_controller.h" #include "core/hle/service/am/applets/applet_error.h" #include "core/hle/service/am/applets/applet_general_backend.h" +#include "core/hle/service/am/applets/applet_mii_edit.h" #include "core/hle/service/am/applets/applet_profile_select.h" #include "core/hle/service/am/applets/applet_software_keyboard.h" #include "core/hle/service/am/applets/applet_web_browser.h" @@ -171,11 +172,12 @@ void Applet::Initialize() { AppletFrontendSet::AppletFrontendSet() = default; AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, + MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_, ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_, WebBrowser web_browser_) : controller{std::move(controller_applet)}, error{std::move(error_applet)}, - parental_controls{std::move(parental_controls_applet)}, + mii_edit{std::move(mii_edit_)}, parental_controls{std::move(parental_controls_applet)}, photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)}, software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {} @@ -202,6 +204,10 @@ void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { frontend.error = std::move(set.error); } + if (set.mii_edit != nullptr) { + frontend.mii_edit = std::move(set.mii_edit); + } + if (set.parental_controls != nullptr) { frontend.parental_controls = std::move(set.parental_controls); } @@ -238,6 +244,10 @@ void AppletManager::SetDefaultAppletsIfMissing() { frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); } + if (frontend.mii_edit == nullptr) { + frontend.mii_edit = std::make_unique<Core::Frontend::DefaultMiiEditApplet>(); + } + if (frontend.parental_controls == nullptr) { frontend.parental_controls = std::make_unique<Core::Frontend::DefaultParentalControlsApplet>(); @@ -277,6 +287,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode return std::make_shared<ProfileSelect>(system, mode, *frontend.profile_select); case AppletId::SoftwareKeyboard: return std::make_shared<SoftwareKeyboard>(system, mode, *frontend.software_keyboard); + case AppletId::MiiEdit: + return std::make_shared<MiiEdit>(system, mode, *frontend.mii_edit); case AppletId::Web: case AppletId::Shop: case AppletId::OfflineWeb: diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 15eeb4ee1..e78a57657 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,7 +9,7 @@ #include "common/swap.h" #include "core/hle/service/kernel_helpers.h" -union ResultCode; +union Result; namespace Core { class System; @@ -20,6 +19,7 @@ namespace Core::Frontend { class ControllerApplet; class ECommerceApplet; class ErrorApplet; +class MiiEditApplet; class ParentalControlsApplet; class PhotoViewerApplet; class ProfileSelectApplet; @@ -138,7 +138,7 @@ public: virtual void Initialize(); virtual bool TransactionComplete() const = 0; - virtual ResultCode GetStatus() const = 0; + virtual Result GetStatus() const = 0; virtual void ExecuteInteractive() = 0; virtual void Execute() = 0; @@ -178,6 +178,7 @@ protected: struct AppletFrontendSet { using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>; using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; + using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>; using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>; using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>; @@ -186,9 +187,9 @@ struct AppletFrontendSet { AppletFrontendSet(); AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, - ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_, - ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_, - WebBrowser web_browser_); + MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet, + PhotoViewer photo_viewer_, ProfileSelect profile_select_, + SoftwareKeyboard software_keyboard_, WebBrowser web_browser_); ~AppletFrontendSet(); AppletFrontendSet(const AppletFrontendSet&) = delete; @@ -199,6 +200,7 @@ struct AppletFrontendSet { ControllerApplet controller; ErrorApplet error; + MiiEdit mii_edit; ParentalControlsApplet parental_controls; PhotoViewer photo_viewer; ProfileSelect profile_select; diff --git a/src/core/hle/service/am/idle.cpp b/src/core/hle/service/am/idle.cpp index 6196773d5..603515284 100644 --- a/src/core/hle/service/am/idle.cpp +++ b/src/core/hle/service/am/idle.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/am/idle.h" diff --git a/src/core/hle/service/am/idle.h b/src/core/hle/service/am/idle.h index e290c30b1..15b31f67e 100644 --- a/src/core/hle/service/am/idle.h +++ b/src/core/hle/service/am/idle.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/am/omm.cpp b/src/core/hle/service/am/omm.cpp index 6da9b9f58..66824e495 100644 --- a/src/core/hle/service/am/omm.cpp +++ b/src/core/hle/service/am/omm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/am/omm.h" diff --git a/src/core/hle/service/am/omm.h b/src/core/hle/service/am/omm.h index 3766150fe..73d0c82d5 100644 --- a/src/core/hle/service/am/omm.h +++ b/src/core/hle/service/am/omm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/am/spsm.cpp b/src/core/hle/service/am/spsm.cpp index 95218d9ee..ec581e32b 100644 --- a/src/core/hle/service/am/spsm.cpp +++ b/src/core/hle/service/am/spsm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/am/spsm.h" diff --git a/src/core/hle/service/am/spsm.h b/src/core/hle/service/am/spsm.h index 04bbf9e68..922f8863e 100644 --- a/src/core/hle/service/am/spsm.h +++ b/src/core/hle/service/am/spsm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/am/tcap.cpp b/src/core/hle/service/am/tcap.cpp index 4d0971c03..818420e22 100644 --- a/src/core/hle/service/am/tcap.cpp +++ b/src/core/hle/service/am/tcap.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/am/tcap.h" diff --git a/src/core/hle/service/am/tcap.h b/src/core/hle/service/am/tcap.h index e9578f16e..6b2148c29 100644 --- a/src/core/hle/service/am/tcap.h +++ b/src/core/hle/service/am/tcap.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 3c83717b5..368ccd52f 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <numeric> diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h index 4b5f7c5f2..6c1ce601a 100644 --- a/src/core/hle/service/aoc/aoc_u.h +++ b/src/core/hle/service/aoc/aoc_u.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp index 243ea15b8..8a338d9b1 100644 --- a/src/core/hle/service/apm/apm.cpp +++ b/src/core/hle/service/apm/apm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/hle/service/apm/apm.h" diff --git a/src/core/hle/service/apm/apm.h b/src/core/hle/service/apm/apm.h index 691fe6c16..0fecc766a 100644 --- a/src/core/hle/service/apm/apm.h +++ b/src/core/hle/service/apm/apm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/apm/apm_controller.cpp b/src/core/hle/service/apm/apm_controller.cpp index 98839fe97..d6de84066 100644 --- a/src/core/hle/service/apm/apm_controller.cpp +++ b/src/core/hle/service/apm/apm_controller.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> @@ -17,8 +16,8 @@ constexpr auto DEFAULT_PERFORMANCE_CONFIGURATION = PerformanceConfiguration::Con Controller::Controller(Core::Timing::CoreTiming& core_timing_) : core_timing{core_timing_}, configs{ - {PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION}, - {PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION}, + {PerformanceMode::Normal, DEFAULT_PERFORMANCE_CONFIGURATION}, + {PerformanceMode::Boost, DEFAULT_PERFORMANCE_CONFIGURATION}, } {} Controller::~Controller() = default; @@ -63,13 +62,13 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) { PerformanceConfiguration::Config15, }}; - SetPerformanceConfiguration(PerformanceMode::Docked, + SetPerformanceConfiguration(PerformanceMode::Boost, BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode))); } PerformanceMode Controller::GetCurrentPerformanceMode() const { - return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked - : PerformanceMode::Handheld; + return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Boost + : PerformanceMode::Normal; } PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) { @@ -81,7 +80,7 @@ PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(Performa } void Controller::SetClockSpeed(u32 mhz) { - LOG_INFO(Service_APM, "called, mhz={:08X}", mhz); + LOG_DEBUG(Service_APM, "called, mhz={:08X}", mhz); // TODO(DarkLordZach): Actually signal core_timing to change clock speed. // TODO(Rodrigo): Remove [[maybe_unused]] when core_timing is used. } diff --git a/src/core/hle/service/apm/apm_controller.h b/src/core/hle/service/apm/apm_controller.h index 8d48e0104..3357b7762 100644 --- a/src/core/hle/service/apm/apm_controller.h +++ b/src/core/hle/service/apm/apm_controller.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -32,15 +31,18 @@ enum class PerformanceConfiguration : u32 { Config16 = 0x9222000C, }; +// This is nn::oe::CpuBoostMode enum class CpuBoostMode : u32 { - Disabled = 0, - Full = 1, // CPU + GPU -> Config 13, 14, 15, or 16 - Partial = 2, // GPU Only -> Config 15 or 16 + Normal = 0, // Boost mode disabled + FastLoad = 1, // CPU + GPU -> Config 13, 14, 15, or 16 + Partial = 2, // GPU Only -> Config 15 or 16 }; -enum class PerformanceMode : u8 { - Handheld = 0, - Docked = 1, +// This is nn::oe::PerformanceMode +enum class PerformanceMode : s32 { + Invalid = -1, + Normal = 0, + Boost = 1, }; // Class to manage the state and change of the emulated system performance. diff --git a/src/core/hle/service/apm/apm_interface.cpp b/src/core/hle/service/apm/apm_interface.cpp index 6163e3294..041fc16bd 100644 --- a/src/core/hle/service/apm/apm_interface.cpp +++ b/src/core/hle/service/apm/apm_interface.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" diff --git a/src/core/hle/service/apm/apm_interface.h b/src/core/hle/service/apm/apm_interface.h index 063ad5308..0740fd4ba 100644 --- a/src/core/hle/service/apm/apm_interface.h +++ b/src/core/hle/service/apm/apm_interface.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp index 260fd0e0e..4a2ae5f88 100644 --- a/src/core/hle/service/audio/audctl.cpp +++ b/src/core/hle/service/audio/audctl.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" diff --git a/src/core/hle/service/audio/audctl.h b/src/core/hle/service/audio/audctl.h index 15f6c77a0..a27ff6cfe 100644 --- a/src/core/hle/service/audio/audctl.h +++ b/src/core/hle/service/audio/audctl.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/auddbg.cpp b/src/core/hle/service/audio/auddbg.cpp index 6264e4bda..5541af300 100644 --- a/src/core/hle/service/audio/auddbg.cpp +++ b/src/core/hle/service/audio/auddbg.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/audio/auddbg.h" diff --git a/src/core/hle/service/audio/auddbg.h b/src/core/hle/service/audio/auddbg.h index d1653eedd..8f26be5dc 100644 --- a/src/core/hle/service/audio/auddbg.h +++ b/src/core/hle/service/audio/auddbg.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/audin_a.cpp b/src/core/hle/service/audio/audin_a.cpp index 10acaad19..98f4a6048 100644 --- a/src/core/hle/service/audio/audin_a.cpp +++ b/src/core/hle/service/audio/audin_a.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/audio/audin_a.h" diff --git a/src/core/hle/service/audio/audin_a.h b/src/core/hle/service/audio/audin_a.h index 15120a4b6..19a927de5 100644 --- a/src/core/hle/service/audio/audin_a.h +++ b/src/core/hle/service/audio/audin_a.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 34cc659ed..48a9a73a0 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp @@ -1,69 +1,211 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#include "audio_core/in/audio_in_system.h" +#include "audio_core/renderer/audio_device.h" +#include "common/common_funcs.h" #include "common/logging/log.h" +#include "common/string_util.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" #include "core/hle/service/audio/audin_u.h" namespace Service::Audio { +using namespace AudioCore::AudioIn; -IAudioIn::IAudioIn(Core::System& system_) - : ServiceFramework{system_, "IAudioIn"}, service_context{system_, "IAudioIn"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "GetAudioInState"}, - {1, &IAudioIn::Start, "Start"}, - {2, nullptr, "Stop"}, - {3, nullptr, "AppendAudioInBuffer"}, - {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"}, - {5, nullptr, "GetReleasedAudioInBuffer"}, - {6, nullptr, "ContainsAudioInBuffer"}, - {7, nullptr, "AppendUacInBuffer"}, - {8, &IAudioIn::AppendAudioInBufferAuto, "AppendAudioInBufferAuto"}, - {9, nullptr, "GetReleasedAudioInBuffersAuto"}, - {10, nullptr, "AppendUacInBufferAuto"}, - {11, nullptr, "GetAudioInBufferCount"}, - {12, nullptr, "SetDeviceGain"}, - {13, nullptr, "GetDeviceGain"}, - {14, nullptr, "FlushAudioInBuffers"}, - }; - // clang-format on +class IAudioIn final : public ServiceFramework<IAudioIn> { +public: + explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id, + std::string& device_name, const AudioInParameter& in_params, u32 handle, + u64 applet_resource_user_id) + : ServiceFramework{system_, "IAudioIn"}, + service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")}, + impl{std::make_shared<In>(system_, manager, event, session_id)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IAudioIn::GetAudioInState, "GetAudioInState"}, + {1, &IAudioIn::Start, "Start"}, + {2, &IAudioIn::Stop, "Stop"}, + {3, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBuffer"}, + {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"}, + {5, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffer"}, + {6, &IAudioIn::ContainsAudioInBuffer, "ContainsAudioInBuffer"}, + {7, &IAudioIn::AppendAudioInBuffer, "AppendUacInBuffer"}, + {8, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBufferAuto"}, + {9, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffersAuto"}, + {10, &IAudioIn::AppendAudioInBuffer, "AppendUacInBufferAuto"}, + {11, &IAudioIn::GetAudioInBufferCount, "GetAudioInBufferCount"}, + {12, &IAudioIn::SetDeviceGain, "SetDeviceGain"}, + {13, &IAudioIn::GetDeviceGain, "GetDeviceGain"}, + {14, &IAudioIn::FlushAudioInBuffers, "FlushAudioInBuffers"}, + }; + // clang-format on - RegisterHandlers(functions); + RegisterHandlers(functions); - buffer_event = service_context.CreateEvent("IAudioIn:BufferEvent"); -} + if (impl->GetSystem() + .Initialize(device_name, in_params, handle, applet_resource_user_id) + .IsError()) { + LOG_ERROR(Service_Audio, "Failed to initialize the AudioIn System!"); + } + } -IAudioIn::~IAudioIn() { - service_context.CloseEvent(buffer_event); -} + ~IAudioIn() override { + impl->Free(); + service_context.CloseEvent(event); + } -void IAudioIn::Start(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + [[nodiscard]] std::shared_ptr<In> GetImpl() { + return impl; + } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} +private: + void GetAudioInState(Kernel::HLERequestContext& ctx) { + const auto state = static_cast<u32>(impl->GetState()); -void IAudioIn::RegisterBufferEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called. State={}", state); - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event->GetReadableEvent()); -} + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(state); + } -void IAudioIn::AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + void Start(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} + auto result = impl->StartSystem(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + void Stop(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + auto result = impl->StopSystem(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + void AppendAudioInBuffer(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u64 tag = rp.PopRaw<u64>(); -AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} { + const auto in_buffer_size{ctx.GetReadBufferSize()}; + if (in_buffer_size < sizeof(AudioInBuffer)) { + LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioInBuffer!"); + } + + const auto& in_buffer = ctx.ReadBuffer(); + AudioInBuffer buffer{}; + std::memcpy(&buffer, in_buffer.data(), sizeof(AudioInBuffer)); + + [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; + LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag); + + auto result = impl->AppendBuffer(buffer, tag); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + auto& buffer_event = impl->GetBufferEvent(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(buffer_event); + } + + void GetReleasedAudioInBuffer(Kernel::HLERequestContext& ctx) { + auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64); + std::vector<u64> released_buffers(write_buffer_size, 0); + + auto count = impl->GetReleasedBuffers(released_buffers); + + [[maybe_unused]] std::string tags{}; + for (u32 i = 0; i < count; i++) { + tags += fmt::format("{:08X}, ", released_buffers[i]); + } + [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; + LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count, + tags); + + ctx.WriteBuffer(released_buffers); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(count); + } + + void ContainsAudioInBuffer(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const u64 tag{rp.Pop<u64>()}; + const auto buffer_queued{impl->ContainsAudioBuffer(tag)}; + + LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(buffer_queued); + } + + void GetAudioInBufferCount(Kernel::HLERequestContext& ctx) { + const auto buffer_count = impl->GetBufferCount(); + + LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count); + + IPC::ResponseBuilder rb{ctx, 3}; + + rb.Push(ResultSuccess); + rb.Push(buffer_count); + } + + void SetDeviceGain(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto volume{rp.Pop<f32>()}; + LOG_DEBUG(Service_Audio, "called. Gain {}", volume); + + impl->SetVolume(volume); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void GetDeviceGain(Kernel::HLERequestContext& ctx) { + auto volume{impl->GetVolume()}; + + LOG_DEBUG(Service_Audio, "called. Gain {}", volume); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(volume); + } + + void FlushAudioInBuffers(Kernel::HLERequestContext& ctx) { + bool flushed{impl->FlushAudioInBuffers()}; + + LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(flushed); + } + + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* event; + std::shared_ptr<AudioCore::AudioIn::In> impl; +}; + +AudInU::AudInU(Core::System& system_) + : ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew}, + service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>( + system_)} { // clang-format off static const FunctionInfo functions[] = { {0, &AudInU::ListAudioIns, "ListAudioIns"}, @@ -81,59 +223,152 @@ AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} { AudInU::~AudInU() = default; void AudInU::ListAudioIns(Kernel::HLERequestContext& ctx) { + using namespace AudioCore::AudioRenderer; + LOG_DEBUG(Service_Audio, "called"); - const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioInDeviceName); - const std::size_t device_count = std::min(count, audio_device_names.size()); - std::vector<AudioInDeviceName> device_names; - device_names.reserve(device_count); + const auto write_count = + static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); + std::vector<AudioDevice::AudioDeviceName> device_names{}; - for (std::size_t i = 0; i < device_count; i++) { - const auto& device_name = audio_device_names[i]; - auto& entry = device_names.emplace_back(); - device_name.copy(entry.data(), device_name.size()); + u32 out_count{0}; + if (write_count > 0) { + out_count = impl->GetDeviceNames(device_names, write_count, false); + ctx.WriteBuffer(device_names); } - ctx.WriteBuffer(device_names); - IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(device_names.size())); + rb.Push(out_count); } void AudInU::ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx) { + using namespace AudioCore::AudioRenderer; + LOG_DEBUG(Service_Audio, "called"); - constexpr u32 device_count = 0; - // Since we don't actually use any other audio input devices, we return 0 devices. Filtered - // device listing just omits the default input device + const auto write_count = + static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); + std::vector<AudioDevice::AudioDeviceName> device_names{}; + + u32 out_count{0}; + if (write_count > 0) { + out_count = impl->GetDeviceNames(device_names, write_count, true); + ctx.WriteBuffer(device_names); + } IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(device_count)); + rb.Push(out_count); } -void AudInU::OpenInOutImpl(Kernel::HLERequestContext& ctx) { - AudInOutParams params{}; - params.channel_count = 2; - params.sample_format = SampleFormat::PCM16; - params.sample_rate = 48000; - params.state = State::Started; +void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto in_params{rp.PopRaw<AudioInParameter>()}; + auto applet_resource_user_id{rp.PopRaw<u64>()}; + const auto device_name_data{ctx.ReadBuffer()}; + auto device_name = Common::StringFromBuffer(device_name_data); + auto handle{ctx.GetCopyHandle(0)}; + + std::scoped_lock l{impl->mutex}; + auto link{impl->LinkToManager()}; + if (link.IsError()) { + LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(link); + return; + } + + size_t new_session_id{}; + auto result{impl->AcquireSessionId(new_session_id)}; + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, + impl->num_free_sessions); + + auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, + in_params, handle, applet_resource_user_id); + impl->sessions[new_session_id] = audio_in->GetImpl(); + impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; + + auto& out_system = impl->sessions[new_session_id]->GetSystem(); + AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(), + .channel_count = out_system.GetChannelCount(), + .sample_format = + static_cast<u32>(out_system.GetSampleFormat()), + .state = static_cast<u32>(out_system.GetState())}; IPC::ResponseBuilder rb{ctx, 6, 0, 1}; - rb.Push(ResultSuccess); - rb.PushRaw<AudInOutParams>(params); - rb.PushIpcInterface<IAudioIn>(system); -} -void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); - OpenInOutImpl(ctx); + std::string out_name{out_system.GetName()}; + ctx.WriteBuffer(out_name); + + rb.Push(ResultSuccess); + rb.PushRaw<AudioInParameterInternal>(out_params); + rb.PushIpcInterface<IAudioIn>(audio_in); } void AudInU::OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); - OpenInOutImpl(ctx); + IPC::RequestParser rp{ctx}; + auto protocol_specified{rp.PopRaw<u64>()}; + auto in_params{rp.PopRaw<AudioInParameter>()}; + auto applet_resource_user_id{rp.PopRaw<u64>()}; + const auto device_name_data{ctx.ReadBuffer()}; + auto device_name = Common::StringFromBuffer(device_name_data); + auto handle{ctx.GetCopyHandle(0)}; + + std::scoped_lock l{impl->mutex}; + auto link{impl->LinkToManager()}; + if (link.IsError()) { + LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(link); + return; + } + + size_t new_session_id{}; + auto result{impl->AcquireSessionId(new_session_id)}; + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, + impl->num_free_sessions); + + auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, + in_params, handle, applet_resource_user_id); + impl->sessions[new_session_id] = audio_in->GetImpl(); + impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; + + auto& out_system = impl->sessions[new_session_id]->GetSystem(); + AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(), + .channel_count = out_system.GetChannelCount(), + .sample_format = + static_cast<u32>(out_system.GetSampleFormat()), + .state = static_cast<u32>(out_system.GetState())}; + + IPC::ResponseBuilder rb{ctx, 6, 0, 1}; + + std::string out_name{out_system.GetName()}; + if (protocol_specified == 0) { + if (out_system.IsUac()) { + out_name = "UacIn"; + } else { + out_name = "DeviceIn"; + } + } + + ctx.WriteBuffer(out_name); + + rb.Push(ResultSuccess); + rb.PushRaw<AudioInParameterInternal>(out_params); + rb.PushIpcInterface<IAudioIn>(audio_in); } } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audin_u.h b/src/core/hle/service/audio/audin_u.h index bf3418613..b45fda78a 100644 --- a/src/core/hle/service/audio/audin_u.h +++ b/src/core/hle/service/audio/audin_u.h @@ -1,9 +1,10 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "audio_core/audio_in_manager.h" +#include "audio_core/in/audio_in.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" @@ -15,22 +16,12 @@ namespace Kernel { class HLERequestContext; } -namespace Service::Audio { - -class IAudioIn final : public ServiceFramework<IAudioIn> { -public: - explicit IAudioIn(Core::System& system_); - ~IAudioIn() override; - -private: - void Start(Kernel::HLERequestContext& ctx); - void RegisterBufferEvent(Kernel::HLERequestContext& ctx); - void AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx); - - KernelHelpers::ServiceContext service_context; +namespace AudioCore::AudioOut { +class Manager; +class In; +} // namespace AudioCore::AudioOut - Kernel::KEvent* buffer_event; -}; +namespace Service::Audio { class AudInU final : public ServiceFramework<AudInU> { public: @@ -38,33 +29,14 @@ public: ~AudInU() override; private: - enum class SampleFormat : u32_le { - PCM16 = 2, - }; - - enum class State : u32_le { - Started = 0, - Stopped = 1, - }; - - struct AudInOutParams { - u32_le sample_rate{}; - u32_le channel_count{}; - SampleFormat sample_format{}; - State state{}; - }; - static_assert(sizeof(AudInOutParams) == 0x10, "AudInOutParams is an invalid size"); - - using AudioInDeviceName = std::array<char, 256>; - static constexpr std::array<std::string_view, 1> audio_device_names{{ - "BuiltInHeadset", - }}; - void ListAudioIns(Kernel::HLERequestContext& ctx); void ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx); void OpenInOutImpl(Kernel::HLERequestContext& ctx); void OpenAudioIn(Kernel::HLERequestContext& ctx); void OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx); + + KernelHelpers::ServiceContext service_context; + std::unique_ptr<AudioCore::AudioIn::Manager> impl; }; } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp index b3f24f9bb..97da71dfa 100644 --- a/src/core/hle/service/audio/audio.cpp +++ b/src/core/hle/service/audio/audio.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/audio/audctl.h" #include "core/hle/service/audio/auddbg.h" diff --git a/src/core/hle/service/audio/audio.h b/src/core/hle/service/audio/audio.h index b6d13912e..bbb2214e4 100644 --- a/src/core/hle/service/audio/audio.h +++ b/src/core/hle/service/audio/audio.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/audout_a.cpp b/src/core/hle/service/audio/audout_a.cpp index 3ee522b50..5ecb99236 100644 --- a/src/core/hle/service/audio/audout_a.cpp +++ b/src/core/hle/service/audio/audout_a.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/audio/audout_a.h" diff --git a/src/core/hle/service/audio/audout_a.h b/src/core/hle/service/audio/audout_a.h index 2043dfb77..f641cffeb 100644 --- a/src/core/hle/service/audio/audout_a.h +++ b/src/core/hle/service/audio/audout_a.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index affa7971c..49c092301 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -1,60 +1,47 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include <cstring> #include <vector> -#include "audio_core/audio_out.h" -#include "audio_core/codec.h" +#include "audio_core/out/audio_out_system.h" +#include "audio_core/renderer/audio_device.h" #include "common/common_funcs.h" #include "common/logging/log.h" +#include "common/string_util.h" #include "common/swap.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" #include "core/hle/service/audio/audout_u.h" #include "core/hle/service/audio/errors.h" -#include "core/hle/service/kernel_helpers.h" #include "core/memory.h" namespace Service::Audio { - -constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}}; -constexpr int DefaultSampleRate{48000}; - -struct AudoutParams { - s32_le sample_rate; - u16_le channel_count; - INSERT_PADDING_BYTES_NOINIT(2); -}; -static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size"); - -enum class AudioState : u32 { - Started, - Stopped, -}; +using namespace AudioCore::AudioOut; class IAudioOut final : public ServiceFramework<IAudioOut> { public: - explicit IAudioOut(Core::System& system_, AudoutParams audio_params_, - AudioCore::AudioOut& audio_core_, std::string&& device_name_, - std::string&& unique_name) - : ServiceFramework{system_, "IAudioOut"}, audio_core{audio_core_}, - device_name{std::move(device_name_)}, audio_params{audio_params_}, - main_memory{system.Memory()}, service_context{system_, "IAudioOut"} { + explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, + size_t session_id, std::string& device_name, + const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) + : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, + service_context{system_, "IAudioOut"}, event{service_context.CreateEvent( + "AudioOutEvent")}, + impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} { + // clang-format off static const FunctionInfo functions[] = { {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, - {1, &IAudioOut::StartAudioOut, "Start"}, - {2, &IAudioOut::StopAudioOut, "Stop"}, - {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"}, + {1, &IAudioOut::Start, "Start"}, + {2, &IAudioOut::Stop, "Stop"}, + {3, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBuffer"}, {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"}, - {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffers"}, + {5, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffers"}, {6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"}, - {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"}, - {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"}, + {7, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBufferAuto"}, + {8, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffersAuto"}, {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"}, {10, &IAudioOut::GetAudioOutPlayedSampleCount, "GetAudioOutPlayedSampleCount"}, {11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"}, @@ -64,241 +51,262 @@ public: // clang-format on RegisterHandlers(functions); - // This is the event handle used to check if the audio buffer was released - buffer_event = service_context.CreateEvent("IAudioOutBufferReleased"); - - stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, - audio_params.channel_count, std::move(unique_name), [this] { - const auto guard = LockService(); - buffer_event->GetWritableEvent().Signal(); - }); + if (impl->GetSystem() + .Initialize(device_name, in_params, handle, applet_resource_user_id) + .IsError()) { + LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!"); + } } ~IAudioOut() override { - service_context.CloseEvent(buffer_event); + impl->Free(); + service_context.CloseEvent(event); } -private: - struct AudioBuffer { - u64_le next; - u64_le buffer; - u64_le buffer_capacity; - u64_le buffer_size; - u64_le offset; - }; - static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size"); + [[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() { + return impl; + } +private: void GetAudioOutState(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto state = static_cast<u32>(impl->GetState()); + + LOG_DEBUG(Service_Audio, "called. State={}", state); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped)); + rb.Push(state); } - void StartAudioOut(Kernel::HLERequestContext& ctx) { + void Start(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); - if (stream->IsPlaying()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_OPERATION_FAILED); - return; - } - - audio_core.StartStream(stream); + auto result = impl->StartSystem(); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } - void StopAudioOut(Kernel::HLERequestContext& ctx) { + void Stop(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); - if (stream->IsPlaying()) { - audio_core.StopStream(stream); - } + auto result = impl->StopSystem(); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } - void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event->GetReadableEvent()); - } - - void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description()); + void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; + u64 tag = rp.PopRaw<u64>(); - const auto& input_buffer{ctx.ReadBuffer()}; - ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer), - "AudioBuffer input is an invalid size!"); - AudioBuffer audio_buffer{}; - std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer)); - const u64 tag{rp.Pop<u64>()}; + const auto in_buffer_size{ctx.GetReadBufferSize()}; + if (in_buffer_size < sizeof(AudioOutBuffer)) { + LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioOutBuffer!"); + } - std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16)); - main_memory.ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size); + const auto& in_buffer = ctx.ReadBuffer(); + AudioOutBuffer buffer{}; + std::memcpy(&buffer, in_buffer.data(), sizeof(AudioOutBuffer)); - if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_BUFFER_COUNT_EXCEEDED); - return; - } + [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; + LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag); + + auto result = impl->AppendBuffer(buffer, tag); IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + auto& buffer_event = impl->GetBufferEvent(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); + rb.PushCopyObjects(buffer_event); } - void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called {}", ctx.Description()); + void GetReleasedAudioOutBuffers(Kernel::HLERequestContext& ctx) { + auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64); + std::vector<u64> released_buffers(write_buffer_size, 0); - const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)}; - const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)}; + auto count = impl->GetReleasedBuffers(released_buffers); - std::vector<u64> tags{released_buffers}; - tags.resize(max_count); - ctx.WriteBuffer(tags); + [[maybe_unused]] std::string tags{}; + for (u32 i = 0; i < count; i++) { + tags += fmt::format("{:08X}, ", released_buffers[i]); + } + [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; + LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count, + tags); + ctx.WriteBuffer(released_buffers); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(static_cast<u32>(released_buffers.size())); + rb.Push(count); } void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); - IPC::RequestParser rp{ctx}; + const u64 tag{rp.Pop<u64>()}; + const auto buffer_queued{impl->ContainsAudioBuffer(tag)}; + + LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(stream->ContainsBuffer(tag)); + rb.Push(buffer_queued); } void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto buffer_count = impl->GetBufferCount(); + + LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count); IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(stream->GetQueueSize())); + rb.Push(buffer_count); } void GetAudioOutPlayedSampleCount(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto samples_played = impl->GetPlayedSampleCount(); + + LOG_DEBUG(Service_Audio, "called. Played samples={}", samples_played); IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); - rb.Push(stream->GetPlayedSampleCount()); + rb.Push(samples_played); } void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + bool flushed{impl->FlushAudioOutBuffers()}; + + LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(stream->Flush()); + rb.Push(flushed); } void SetAudioOutVolume(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const float volume = rp.Pop<float>(); - LOG_DEBUG(Service_Audio, "called, volume={}", volume); + const auto volume = rp.Pop<f32>(); + + LOG_DEBUG(Service_Audio, "called. Volume={}", volume); - stream->SetVolume(volume); + impl->SetVolume(volume); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void GetAudioOutVolume(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto volume = impl->GetVolume(); + + LOG_DEBUG(Service_Audio, "called. Volume={}", volume); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(stream->GetVolume()); + rb.Push(volume); } - AudioCore::AudioOut& audio_core; - AudioCore::StreamPtr stream; - std::string device_name; - - [[maybe_unused]] AudoutParams audio_params{}; - - Core::Memory::Memory& main_memory; - KernelHelpers::ServiceContext service_context; - - /// This is the event handle used to check if the audio buffer was released - Kernel::KEvent* buffer_event; + Kernel::KEvent* event; + std::shared_ptr<AudioCore::AudioOut::Out> impl; }; -AudOutU::AudOutU(Core::System& system_) : ServiceFramework{system_, "audout:u"} { +AudOutU::AudOutU(Core::System& system_) + : ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew}, + service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>( + system_)} { // clang-format off static const FunctionInfo functions[] = { - {0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"}, - {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"}, - {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"}, - {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}, + {0, &AudOutU::ListAudioOuts, "ListAudioOuts"}, + {1, &AudOutU::OpenAudioOut, "OpenAudioOut"}, + {2, &AudOutU::ListAudioOuts, "ListAudioOutsAuto"}, + {3, &AudOutU::OpenAudioOut, "OpenAudioOutAuto"}, }; // clang-format on RegisterHandlers(functions); - audio_core = std::make_unique<AudioCore::AudioOut>(); } AudOutU::~AudOutU() = default; -void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); +void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { + using namespace AudioCore::AudioRenderer; - ctx.WriteBuffer(DefaultDevice); + std::scoped_lock l{impl->mutex}; + + const auto write_count = + static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); + std::vector<AudioDevice::AudioDeviceName> device_names{}; + if (write_count > 0) { + device_names.emplace_back("DeviceOut"); + LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut"); + } else { + LOG_DEBUG(Service_Audio, "called. Empty buffer passed in."); + } + + ctx.WriteBuffer(device_names); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(1); // Amount of audio devices + rb.Push<u32>(static_cast<u32>(device_names.size())); } -void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); - +void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto in_params{rp.PopRaw<AudioOutParameter>()}; + auto applet_resource_user_id{rp.PopRaw<u64>()}; const auto device_name_data{ctx.ReadBuffer()}; - std::string device_name; - if (device_name_data[0] != '\0') { - device_name.assign(device_name_data.begin(), device_name_data.end()); - } else { - device_name.assign(DefaultDevice.begin(), DefaultDevice.end()); - } - ctx.WriteBuffer(device_name); + auto device_name = Common::StringFromBuffer(device_name_data); + auto handle{ctx.GetCopyHandle(0)}; - IPC::RequestParser rp{ctx}; - auto params{rp.PopRaw<AudoutParams>()}; - if (params.channel_count <= 2) { - // Mono does not exist for audout - params.channel_count = 2; - } else { - params.channel_count = 6; + auto link{impl->LinkToManager()}; + if (link.IsError()) { + LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(link); + return; } - if (!params.sample_rate) { - params.sample_rate = DefaultSampleRate; + + size_t new_session_id{}; + auto result{impl->AcquireSessionId(new_session_id)}; + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; } - std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())}; - auto audio_out_interface = std::make_shared<IAudioOut>( - system, params, *audio_core, std::move(device_name), std::move(unique_name)); + LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id, + impl->num_free_sessions); + + auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name, + in_params, handle, applet_resource_user_id); + + impl->sessions[new_session_id] = audio_out->GetImpl(); + impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; + + auto& out_system = impl->sessions[new_session_id]->GetSystem(); + AudioOutParameterInternal out_params{.sample_rate = out_system.GetSampleRate(), + .channel_count = out_system.GetChannelCount(), + .sample_format = + static_cast<u32>(out_system.GetSampleFormat()), + .state = static_cast<u32>(out_system.GetState())}; IPC::ResponseBuilder rb{ctx, 6, 0, 1}; - rb.Push(ResultSuccess); - rb.Push<u32>(DefaultSampleRate); - rb.Push<u32>(params.channel_count); - rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16)); - rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); - rb.PushIpcInterface<IAudioOut>(audio_out_interface); - audio_out_interfaces.push_back(std::move(audio_out_interface)); + ctx.WriteBuffer(out_system.GetName()); + + rb.Push(ResultSuccess); + rb.PushRaw<AudioOutParameterInternal>(out_params); + rb.PushIpcInterface<IAudioOut>(audio_out); } } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index f7ae2f2bf..fdc0ee754 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h @@ -1,16 +1,13 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <vector> +#include "audio_core/audio_out_manager.h" +#include "audio_core/out/audio_out.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" -namespace AudioCore { -class AudioOut; -} - namespace Core { class System; } @@ -19,6 +16,11 @@ namespace Kernel { class HLERequestContext; } +namespace AudioCore::AudioOut { +class Manager; +class Out; +} // namespace AudioCore::AudioOut + namespace Service::Audio { class IAudioOut; @@ -29,11 +31,11 @@ public: ~AudOutU() override; private: - void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); - void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); + void ListAudioOuts(Kernel::HLERequestContext& ctx); + void OpenAudioOut(Kernel::HLERequestContext& ctx); - std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces; - std::unique_ptr<AudioCore::AudioOut> audio_core; + KernelHelpers::ServiceContext service_context; + std::unique_ptr<AudioCore::AudioOut::Manager> impl; }; } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audrec_a.cpp b/src/core/hle/service/audio/audrec_a.cpp index 70fc17ae2..fa82e9ac7 100644 --- a/src/core/hle/service/audio/audrec_a.cpp +++ b/src/core/hle/service/audio/audrec_a.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/audio/audrec_a.h" diff --git a/src/core/hle/service/audio/audrec_a.h b/src/core/hle/service/audio/audrec_a.h index 2cce90b1d..9edf89f6c 100644 --- a/src/core/hle/service/audio/audrec_a.h +++ b/src/core/hle/service/audio/audrec_a.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/audrec_u.cpp b/src/core/hle/service/audio/audrec_u.cpp index 74a65ccff..bc55cec17 100644 --- a/src/core/hle/service/audio/audrec_u.cpp +++ b/src/core/hle/service/audio/audrec_u.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/audio/audrec_u.h" diff --git a/src/core/hle/service/audio/audrec_u.h b/src/core/hle/service/audio/audrec_u.h index f79d49e5c..8b4817884 100644 --- a/src/core/hle/service/audio/audrec_u.h +++ b/src/core/hle/service/audio/audrec_u.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/audren_a.cpp b/src/core/hle/service/audio/audren_a.cpp index cf8c34a15..e775ac3bf 100644 --- a/src/core/hle/service/audio/audren_a.cpp +++ b/src/core/hle/service/audio/audren_a.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/audio/audren_a.h" diff --git a/src/core/hle/service/audio/audren_a.h b/src/core/hle/service/audio/audren_a.h index 5d0a626ad..9e08b4245 100644 --- a/src/core/hle/service/audio/audren_a.h +++ b/src/core/hle/service/audio/audren_a.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index f45e5cecc..6fb07c37d 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -1,11 +1,15 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include <memory> -#include "audio_core/audio_renderer.h" +#include "audio_core/audio_core.h" +#include "audio_core/common/audio_renderer_parameter.h" +#include "audio_core/common/feature_support.h" +#include "audio_core/renderer/audio_device.h" +#include "audio_core/renderer/audio_renderer.h" +#include "audio_core/renderer/voice/voice_info.h" #include "common/alignment.h" #include "common/bit_util.h" #include "common/common_funcs.h" @@ -14,90 +18,127 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/service/audio/audren_u.h" #include "core/hle/service/audio/errors.h" +#include "core/memory.h" + +using namespace AudioCore::AudioRenderer; namespace Service::Audio { class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { public: - explicit IAudioRenderer(Core::System& system_, - const AudioCommon::AudioRendererParameter& audren_params, - const std::size_t instance_number) - : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"} { + explicit IAudioRenderer(Core::System& system_, Manager& manager_, + AudioCore::AudioRendererParameterInternal& params, + Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, + u32 process_handle, u64 applet_resource_user_id, s32 session_id) + : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew}, + service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent( + "IAudioRendererEvent")}, + manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} { // clang-format off static const FunctionInfo functions[] = { {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"}, {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"}, {3, &IAudioRenderer::GetState, "GetState"}, - {4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"}, + {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"}, {5, &IAudioRenderer::Start, "Start"}, {6, &IAudioRenderer::Stop, "Stop"}, {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"}, {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, - {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"}, - {11, &IAudioRenderer::ExecuteAudioRendererRendering, "ExecuteAudioRendererRendering"}, + {10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"}, + {11, nullptr, "ExecuteAudioRendererRendering"}, }; // clang-format on RegisterHandlers(functions); - system_event = service_context.CreateEvent("IAudioRenderer:SystemEvent"); - renderer = std::make_unique<AudioCore::AudioRenderer>( - system.CoreTiming(), system.Memory(), audren_params, - [this]() { - const auto guard = LockService(); - system_event->GetWritableEvent().Signal(); - }, - instance_number); + impl->Initialize(params, transfer_memory, transfer_memory_size, process_handle, + applet_resource_user_id, session_id); } ~IAudioRenderer() override { - service_context.CloseEvent(system_event); + impl->Finalize(); + service_context.CloseEvent(rendered_event); } private: void GetSampleRate(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto sample_rate{impl->GetSystem().GetSampleRate()}; + + LOG_DEBUG(Service_Audio, "called. Sample rate {}", sample_rate); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(renderer->GetSampleRate()); + rb.Push(sample_rate); } void GetSampleCount(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto sample_count{impl->GetSystem().GetSampleCount()}; + + LOG_DEBUG(Service_Audio, "called. Sample count {}", sample_count); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(renderer->GetSampleCount()); + rb.Push(sample_count); } void GetState(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const u32 state{!impl->GetSystem().IsActive()}; + + LOG_DEBUG(Service_Audio, "called, state {}", state); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(static_cast<u32>(renderer->GetStreamState())); + rb.Push(state); } void GetMixBufferCount(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); + const auto buffer_count{impl->GetSystem().GetMixBufferCount()}; + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(renderer->GetMixBufferCount()); + rb.Push(buffer_count); } - void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "(STUBBED) called"); + void RequestUpdate(Kernel::HLERequestContext& ctx) { + LOG_TRACE(Service_Audio, "called"); + + std::vector<u8> input{ctx.ReadBuffer(0)}; + + // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for + // checking size 0. Performance size is 0 for most games. + + std::vector<u8> output{}; + std::vector<u8> performance{}; + auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0}; + if (is_buffer_b) { + const auto buffersB{ctx.BufferDescriptorB()}; + output.resize(buffersB[0].Size(), 0); + performance.resize(buffersB[1].Size(), 0); + } else { + const auto buffersC{ctx.BufferDescriptorC()}; + output.resize(buffersC[0].Size(), 0); + performance.resize(buffersC[1].Size(), 0); + } - std::vector<u8> output_params(ctx.GetWriteBufferSize(), 0); - auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params); + auto result = impl->RequestUpdate(input, performance, output); if (result.IsSuccess()) { - ctx.WriteBuffer(output_params); + if (is_buffer_b) { + ctx.WriteBufferB(output.data(), output.size(), 0); + ctx.WriteBufferB(performance.data(), performance.size(), 1); + } else { + ctx.WriteBufferC(output.data(), output.size(), 0); + ctx.WriteBufferC(performance.data(), performance.size(), 1); + } + } else { + LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); } IPC::ResponseBuilder rb{ctx, 2}; @@ -105,38 +146,45 @@ private: } void Start(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); - const auto result = renderer->Start(); + impl->Start(); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + rb.Push(ResultSuccess); } void Stop(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); - const auto result = renderer->Stop(); + impl->Stop(); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + rb.Push(ResultSuccess); } void QuerySystemEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); + + if (impl->GetSystem().GetExecutionMode() == AudioCore::ExecutionMode::Manual) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NOT_SUPPORTED); + return; + } IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(system_event->GetReadableEvent()); + rb.PushCopyObjects(rendered_event->GetReadableEvent()); } void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + IPC::RequestParser rp{ctx}; - rendering_time_limit_percent = rp.Pop<u32>(); - LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}", - rendering_time_limit_percent); + auto limit = rp.PopRaw<u32>(); - ASSERT(rendering_time_limit_percent <= 100); + auto& system_ = impl->GetSystem(); + system_.SetRenderingTimeLimit(limit); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -145,34 +193,34 @@ private: void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); + auto& system_ = impl->GetSystem(); + auto time = system_.GetRenderingTimeLimit(); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(rendering_time_limit_percent); + rb.Push(time); } void ExecuteAudioRendererRendering(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); - - // This service command currently only reports an unsupported operation - // error code, or aborts. Given that, we just always return an error - // code in this case. - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_SUPPORTED); } KernelHelpers::ServiceContext service_context; - - Kernel::KEvent* system_event; - std::unique_ptr<AudioCore::AudioRenderer> renderer; - u32 rendering_time_limit_percent = 100; + Kernel::KEvent* rendered_event; + Manager& manager; + std::unique_ptr<Renderer> impl; }; class IAudioDevice final : public ServiceFramework<IAudioDevice> { + public: - explicit IAudioDevice(Core::System& system_, Kernel::KEvent* buffer_event_, u32_le revision_) - : ServiceFramework{system_, "IAudioDevice"}, buffer_event{buffer_event_}, revision{ - revision_} { + explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision, + u32 device_num) + : ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew}, + service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>( + system_, applet_resource_user_id, + revision)}, + event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} { static const FunctionInfo functions[] = { {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, {1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"}, @@ -186,54 +234,45 @@ public: {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"}, {11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"}, {12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"}, - {13, nullptr, "GetActiveAudioOutputDeviceName"}, - {14, nullptr, "ListAudioOutputDeviceName"}, + {13, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioOutputDeviceName"}, + {14, &IAudioDevice::ListAudioOutputDeviceName, "ListAudioOutputDeviceName"}, }; RegisterHandlers(functions); + + event->GetWritableEvent().Signal(); } -private: - using AudioDeviceName = std::array<char, 256>; - static constexpr std::array<std::string_view, 4> audio_device_names{{ - "AudioStereoJackOutput", - "AudioBuiltInSpeakerOutput", - "AudioTvOutput", - "AudioUsbDeviceOutput", - }}; - enum class DeviceType { - AHUBHeadphones, - AHUBSpeakers, - HDA, - USBOutput, - }; + ~IAudioDevice() override { + service_context.CloseEvent(event); + } +private: void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName); - const bool usb_output_supported = - IsFeatureSupported(AudioFeatures::AudioUSBDeviceOutput, revision); - const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioDeviceName); + std::vector<AudioDevice::AudioDeviceName> out_names{}; - std::vector<AudioDeviceName> name_buffer; - name_buffer.reserve(audio_device_names.size()); + const u32 out_count = impl->ListAudioDeviceName(out_names, in_count); - for (std::size_t i = 0; i < count && i < audio_device_names.size(); i++) { - const auto type = static_cast<DeviceType>(i); - - if (!usb_output_supported && type == DeviceType::USBOutput) { - continue; + std::string out{}; + for (u32 i = 0; i < out_count; i++) { + std::string a{}; + u32 j = 0; + while (out_names[i].name[j] != '\0') { + a += out_names[i].name[j]; + j++; } - - const auto& device_name = audio_device_names[i]; - auto& entry = name_buffer.emplace_back(); - device_name.copy(entry.data(), device_name.size()); + out += "\n\t" + a; } - ctx.WriteBuffer(name_buffer); + LOG_DEBUG(Service_Audio, "called.\nNames={}", out); IPC::ResponseBuilder rb{ctx, 3}; + + ctx.WriteBuffer(out_names); + rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(name_buffer.size())); + rb.Push(out_count); } void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { @@ -243,7 +282,11 @@ private: const auto device_name_buffer = ctx.ReadBuffer(); const std::string name = Common::StringFromBuffer(device_name_buffer); - LOG_WARNING(Service_Audio, "(STUBBED) called. name={}, volume={}", name, volume); + LOG_DEBUG(Service_Audio, "called. name={}, volume={}", name, volume); + + if (name == "AudioTvOutput") { + impl->SetDeviceVolumes(volume); + } IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -253,53 +296,60 @@ private: const auto device_name_buffer = ctx.ReadBuffer(); const std::string name = Common::StringFromBuffer(device_name_buffer); - LOG_WARNING(Service_Audio, "(STUBBED) called. name={}", name); + LOG_DEBUG(Service_Audio, "called. Name={}", name); + + f32 volume{1.0f}; + if (name == "AudioTvOutput") { + volume = impl->GetDeviceVolume(name); + } IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(1.0f); + rb.Push(volume); } void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + const auto write_size = ctx.GetWriteBufferSize() / sizeof(char); + std::string out_name{"AudioTvOutput"}; - // Currently set to always be TV audio output. - const auto& device_name = audio_device_names[2]; + LOG_DEBUG(Service_Audio, "(STUBBED) called. Name={}", out_name); - AudioDeviceName out_device_name{}; - device_name.copy(out_device_name.data(), device_name.size()); + out_name.resize(write_size); - ctx.WriteBuffer(out_device_name); + ctx.WriteBuffer(out_name); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "(STUBBED) called"); - buffer_event->GetWritableEvent().Signal(); + event->GetWritableEvent().Signal(); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event->GetReadableEvent()); + rb.PushCopyObjects(event->GetReadableEvent()); } void GetActiveChannelCount(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + const auto& sink{system.AudioCore().GetOutputSink()}; + u32 channel_count{sink.GetDeviceChannels()}; + + LOG_DEBUG(Service_Audio, "(STUBBED) called. Channels={}", channel_count); IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); - rb.Push<u32>(2); + rb.Push<u32>(channel_count); } - // Should be similar to QueryAudioDeviceOutputEvent void QueryAudioDeviceInputEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event->GetReadableEvent()); + rb.PushCopyObjects(event->GetReadableEvent()); } void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) { @@ -307,402 +357,167 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event->GetReadableEvent()); + rb.PushCopyObjects(event->GetReadableEvent()); } - Kernel::KEvent* buffer_event; - u32_le revision = 0; + void ListAudioOutputDeviceName(Kernel::HLERequestContext& ctx) { + const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName); + + std::vector<AudioDevice::AudioDeviceName> out_names{}; + + const u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count); + + std::string out{}; + for (u32 i = 0; i < out_count; i++) { + std::string a{}; + u32 j = 0; + while (out_names[i].name[j] != '\0') { + a += out_names[i].name[j]; + j++; + } + out += "\n\t" + a; + } + + LOG_DEBUG(Service_Audio, "called.\nNames={}", out); + + IPC::ResponseBuilder rb{ctx, 3}; + + ctx.WriteBuffer(out_names); + + rb.Push(ResultSuccess); + rb.Push(out_count); + } + + KernelHelpers::ServiceContext service_context; + std::unique_ptr<AudioDevice> impl; + Kernel::KEvent* event; }; AudRenU::AudRenU(Core::System& system_) - : ServiceFramework{system_, "audren:u"}, service_context{system_, "audren:u"} { + : ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew}, + service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} { // clang-format off static const FunctionInfo functions[] = { {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"}, - {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetWorkBufferSize"}, + {1, &AudRenU::GetWorkBufferSize, "GetWorkBufferSize"}, {2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"}, - {3, &AudRenU::OpenAudioRendererForManualExecution, "OpenAudioRendererForManualExecution"}, + {3, nullptr, "OpenAudioRendererForManualExecution"}, {4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"}, }; // clang-format on RegisterHandlers(functions); - - buffer_event = service_context.CreateEvent("IAudioOutBufferReleasedEvent"); } -AudRenU::~AudRenU() { - service_context.CloseEvent(buffer_event); -} +AudRenU::~AudRenU() = default; void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); - - OpenAudioRendererImpl(ctx); -} - -static u64 CalculateNumPerformanceEntries(const AudioCommon::AudioRendererParameter& params) { - // +1 represents the final mix. - return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count + - 1; -} - -void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); - - // Several calculations below align the sizes being calculated - // onto a 64 byte boundary. - static constexpr u64 buffer_alignment_size = 64; - - // Some calculations that calculate portions of the buffer - // that will contain information, on the other hand, align - // the result of some of their calcularions on a 16 byte boundary. - static constexpr u64 info_field_alignment_size = 16; - - // Maximum detail entries that may exist at one time for performance - // frame statistics. - static constexpr u64 max_perf_detail_entries = 100; - - // Size of the data structure representing the bulk of the voice-related state. - static constexpr u64 voice_state_size_bytes = 0x100; - - // Size of the upsampler manager data structure - constexpr u64 upsampler_manager_size = 0x48; - - // Calculates the part of the size that relates to mix buffers. - const auto calculate_mix_buffer_sizes = [](const AudioCommon::AudioRendererParameter& params) { - // As of 8.0.0 this is the maximum on voice channels. - constexpr u64 max_voice_channels = 6; - - // The service expects the sample_count member of the parameters to either be - // a value of 160 or 240, so the maximum sample count is assumed in order - // to adequately handle all values at runtime. - constexpr u64 default_max_sample_count = 240; - - const u64 total_mix_buffers = params.mix_buffer_count + max_voice_channels; - - u64 size = 0; - size += total_mix_buffers * (sizeof(s32) * params.sample_count); - size += total_mix_buffers * (sizeof(s32) * default_max_sample_count); - size += u64{params.submix_count} + params.sink_count; - size = Common::AlignUp(size, buffer_alignment_size); - size += Common::AlignUp(params.unknown_30, buffer_alignment_size); - size += Common::AlignUp(sizeof(s32) * params.mix_buffer_count, buffer_alignment_size); - return size; - }; - - // Calculates the portion of the size related to the mix data (and the sorting thereof). - const auto calculate_mix_info_size = [](const AudioCommon::AudioRendererParameter& params) { - // The size of the mixing info data structure. - constexpr u64 mix_info_size = 0x940; - - // Consists of total submixes with the final mix included. - const u64 total_mix_count = u64{params.submix_count} + 1; - - // The total number of effects that may be available to the audio renderer at any time. - constexpr u64 max_effects = 256; - - // Calculates the part of the size related to the audio node state. - // This will only be used if the audio revision supports the splitter. - const auto calculate_node_state_size = [](std::size_t num_nodes) { - // Internally within a nodestate, it appears to use a data structure - // similar to a std::bitset<64> twice. - constexpr u64 bit_size = Common::BitSize<u64>(); - constexpr u64 num_bitsets = 2; - - // Node state instances have three states internally for performing - // depth-first searches of nodes. Initialized, Found, and Done Sorting. - constexpr u64 num_states = 3; - - u64 size = 0; - size += (num_nodes * num_nodes) * sizeof(s32); - size += num_states * (num_nodes * sizeof(s32)); - size += num_bitsets * (Common::AlignUp(num_nodes, bit_size) / Common::BitSize<u8>()); - return size; - }; - - // Calculates the part of the size related to the adjacency (aka edge) matrix. - const auto calculate_edge_matrix_size = [](std::size_t num_nodes) { - return (num_nodes * num_nodes) * sizeof(s32); - }; - - u64 size = 0; - size += Common::AlignUp(sizeof(void*) * total_mix_count, info_field_alignment_size); - size += Common::AlignUp(mix_info_size * total_mix_count, info_field_alignment_size); - size += Common::AlignUp(sizeof(s32) * max_effects * params.submix_count, - info_field_alignment_size); - - if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { - size += Common::AlignUp(calculate_node_state_size(total_mix_count) + - calculate_edge_matrix_size(total_mix_count), - info_field_alignment_size); - } - - return size; - }; - - // Calculates the part of the size related to voice channel info. - const auto calculate_voice_info_size = [](const AudioCommon::AudioRendererParameter& params) { - constexpr u64 voice_info_size = 0x220; - constexpr u64 voice_resource_size = 0xD0; - - u64 size = 0; - size += Common::AlignUp(sizeof(void*) * params.voice_count, info_field_alignment_size); - size += Common::AlignUp(voice_info_size * params.voice_count, info_field_alignment_size); - size += - Common::AlignUp(voice_resource_size * params.voice_count, info_field_alignment_size); - size += - Common::AlignUp(voice_state_size_bytes * params.voice_count, info_field_alignment_size); - return size; - }; - - // Calculates the part of the size related to memory pools. - const auto calculate_memory_pools_size = [](const AudioCommon::AudioRendererParameter& params) { - const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count); - const u64 memory_pool_info_size = 0x20; - return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size); - }; - - // Calculates the part of the size related to the splitter context. - const auto calculate_splitter_context_size = - [](const AudioCommon::AudioRendererParameter& params) -> u64 { - if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { - return 0; - } - - constexpr u64 splitter_info_size = 0x20; - constexpr u64 splitter_destination_data_size = 0xE0; - - u64 size = 0; - size += params.num_splitter_send_channels; - size += - Common::AlignUp(splitter_info_size * params.splitter_count, info_field_alignment_size); - size += Common::AlignUp(splitter_destination_data_size * params.num_splitter_send_channels, - info_field_alignment_size); - - return size; - }; - - // Calculates the part of the size related to the upsampler info. - const auto calculate_upsampler_info_size = - [](const AudioCommon::AudioRendererParameter& params) { - constexpr u64 upsampler_info_size = 0x280; - // Yes, using the buffer size over info alignment size is intentional here. - return Common::AlignUp(upsampler_info_size * - (u64{params.submix_count} + params.sink_count), - buffer_alignment_size); - }; - - // Calculates the part of the size related to effect info. - const auto calculate_effect_info_size = [](const AudioCommon::AudioRendererParameter& params) { - constexpr u64 effect_info_size = 0x2B0; - return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size); - }; - - // Calculates the part of the size related to audio sink info. - const auto calculate_sink_info_size = [](const AudioCommon::AudioRendererParameter& params) { - const u64 sink_info_size = 0x170; - return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size); - }; - - // Calculates the part of the size related to voice state info. - const auto calculate_voice_state_size = [](const AudioCommon::AudioRendererParameter& params) { - const u64 voice_state_size = 0x100; - const u64 additional_size = buffer_alignment_size - 1; - return Common::AlignUp(voice_state_size * params.voice_count + additional_size, - info_field_alignment_size); - }; - - // Calculates the part of the size related to performance statistics. - const auto calculate_perf_size = [](const AudioCommon::AudioRendererParameter& params) { - // Extra size value appended to the end of the calculation. - constexpr u64 appended = 128; - - // Whether or not we assume the newer version of performance metrics data structures. - const bool is_v2 = - IsFeatureSupported(AudioFeatures::PerformanceMetricsVersion2, params.revision); - - // Data structure sizes - constexpr u64 perf_statistics_size = 0x0C; - const u64 header_size = is_v2 ? 0x30 : 0x18; - const u64 entry_size = is_v2 ? 0x18 : 0x10; - const u64 detail_size = is_v2 ? 0x18 : 0x10; - - const u64 entry_count = CalculateNumPerformanceEntries(params); - const u64 size_per_frame = - header_size + (entry_size * entry_count) + (detail_size * max_perf_detail_entries); - - u64 size = 0; - size += Common::AlignUp(size_per_frame * params.performance_frame_count + 1, - buffer_alignment_size); - size += Common::AlignUp(perf_statistics_size, buffer_alignment_size); - size += appended; - return size; - }; - - // Calculates the part of the size that relates to the audio command buffer. - const auto calculate_command_buffer_size = - [](const AudioCommon::AudioRendererParameter& params) { - constexpr u64 alignment = (buffer_alignment_size - 1) * 2; - - if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) { - constexpr u64 command_buffer_size = 0x18000; - - return command_buffer_size + alignment; - } - - // When the variadic command buffer is supported, this means - // the command generator for the audio renderer can issue commands - // that are (as one would expect), variable in size. So what we need to do - // is determine the maximum possible size for a few command data structures - // then multiply them by the amount of present commands indicated by the given - // respective audio parameters. - - constexpr u64 max_biquad_filters = 2; - constexpr u64 max_mix_buffers = 24; - - constexpr u64 biquad_filter_command_size = 0x2C; - - constexpr u64 depop_mix_command_size = 0x24; - constexpr u64 depop_setup_command_size = 0x50; - - constexpr u64 effect_command_max_size = 0x540; - - constexpr u64 mix_command_size = 0x1C; - constexpr u64 mix_ramp_command_size = 0x24; - constexpr u64 mix_ramp_grouped_command_size = 0x13C; - - constexpr u64 perf_command_size = 0x28; - - constexpr u64 sink_command_size = 0x130; - - constexpr u64 submix_command_max_size = - depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers; - - constexpr u64 volume_command_size = 0x1C; - constexpr u64 volume_ramp_command_size = 0x20; - - constexpr u64 voice_biquad_filter_command_size = - biquad_filter_command_size * max_biquad_filters; - constexpr u64 voice_data_command_size = 0x9C; - const u64 voice_command_max_size = - (params.splitter_count * depop_setup_command_size) + - (voice_data_command_size + voice_biquad_filter_command_size + - volume_ramp_command_size + mix_ramp_grouped_command_size); + IPC::RequestParser rp{ctx}; - // Now calculate the individual elements that comprise the size and add them together. - const u64 effect_commands_size = params.effect_count * effect_command_max_size; + AudioCore::AudioRendererParameterInternal params; + rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params); + auto transfer_memory_handle = ctx.GetCopyHandle(0); + auto process_handle = ctx.GetCopyHandle(1); + auto transfer_memory_size = rp.Pop<u64>(); + auto applet_resource_user_id = rp.Pop<u64>(); - const u64 final_mix_commands_size = - depop_mix_command_size + volume_command_size * max_mix_buffers; + if (impl->GetSessionCount() + 1 > AudioCore::MaxRendererSessions) { + LOG_ERROR(Service_Audio, "Too many AudioRenderer sessions open!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_MAXIMUM_SESSIONS_REACHED); + return; + } - const u64 perf_commands_size = - perf_command_size * - (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); + const auto& handle_table{system.CurrentProcess()->GetHandleTable()}; + auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)}; + auto transfer_memory{ + process->GetHandleTable().GetObject<Kernel::KTransferMemory>(transfer_memory_handle)}; - const u64 sink_commands_size = params.sink_count * sink_command_size; + const auto session_id{impl->GetSessionId()}; + if (session_id == -1) { + LOG_ERROR(Service_Audio, "Tried to open a session that's already in use!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_MAXIMUM_SESSIONS_REACHED); + return; + } - const u64 splitter_commands_size = - params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; + LOG_DEBUG(Service_Audio, "Opened new AudioRenderer session {} sessions open {}", session_id, + impl->GetSessionCount()); - const u64 submix_commands_size = params.submix_count * submix_command_max_size; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IAudioRenderer>(system, *impl, params, transfer_memory.GetPointerUnsafe(), + transfer_memory_size, process_handle, + applet_resource_user_id, session_id); +} - const u64 voice_commands_size = params.voice_count * voice_command_max_size; - - return effect_commands_size + final_mix_commands_size + perf_commands_size + - sink_commands_size + splitter_commands_size + submix_commands_size + - voice_commands_size + alignment; - }; +void AudRenU::GetWorkBufferSize(Kernel::HLERequestContext& ctx) { + AudioCore::AudioRendererParameterInternal params; IPC::RequestParser rp{ctx}; - const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>(); - - u64 size = 0; - size += calculate_mix_buffer_sizes(params); - size += calculate_mix_info_size(params); - size += calculate_voice_info_size(params); - size += upsampler_manager_size; - size += calculate_memory_pools_size(params); - size += calculate_splitter_context_size(params); - - size = Common::AlignUp(size, buffer_alignment_size); - - size += calculate_upsampler_info_size(params); - size += calculate_effect_info_size(params); - size += calculate_sink_info_size(params); - size += calculate_voice_state_size(params); - size += calculate_perf_size(params); - size += calculate_command_buffer_size(params); - - // finally, 4KB page align the size, and we're done. - size = Common::AlignUp(size, 4096); + rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params); + + u64 size{0}; + auto result = impl->GetWorkBufferSize(params, size); + + std::string output_info{}; + output_info += fmt::format("\tRevision {}", AudioCore::GetRevisionNum(params.revision)); + output_info += + fmt::format("\n\tSample Rate {}, Sample Count {}", params.sample_rate, params.sample_count); + output_info += fmt::format("\n\tExecution Mode {}, Voice Drop Enabled {}", + static_cast<u32>(params.execution_mode), params.voice_drop_enabled); + output_info += fmt::format( + "\n\tSizes: Effects {:04X}, Mixes {:04X}, Sinks {:04X}, Submixes {:04X}, Splitter Infos " + "{:04X}, Splitter Destinations {:04X}, Voices {:04X}, Performance Frames {:04X} External " + "Context {:04X}", + params.effects, params.mixes, params.sinks, params.sub_mixes, params.splitter_infos, + params.splitter_destinations, params.voices, params.perf_frames, + params.external_context_size); + + LOG_DEBUG(Service_Audio, "called.\nInput params:\n{}\nOutput params:\n\tWorkbuffer size {:08X}", + output_info, size); IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); + rb.Push(result); rb.Push<u64>(size); - - LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", size); } void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const u64 aruid = rp.Pop<u64>(); - LOG_DEBUG(Service_Audio, "called. aruid={:016X}", aruid); + const auto applet_resource_user_id = rp.Pop<u64>(); + + LOG_DEBUG(Service_Audio, "called. Applet resource id {}", applet_resource_user_id); - // Revisionless variant of GetAudioDeviceServiceWithRevisionInfo that - // always assumes the initial release revision (REV1). IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); - rb.PushIpcInterface<IAudioDevice>(system, buffer_event, Common::MakeMagic('R', 'E', 'V', '1')); + rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id, + ::Common::MakeMagic('R', 'E', 'V', '1'), num_audio_devices++); } void AudRenU::OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); - - OpenAudioRendererImpl(ctx); } void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) { struct Parameters { u32 revision; - u64 aruid; + u64 applet_resource_user_id; }; IPC::RequestParser rp{ctx}; - const auto [revision, aruid] = rp.PopRaw<Parameters>(); - LOG_DEBUG(Service_Audio, "called. revision={:08X}, aruid={:016X}", revision, aruid); + const auto [revision, applet_resource_user_id] = rp.PopRaw<Parameters>(); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IAudioDevice>(system, buffer_event, revision); -} + LOG_DEBUG(Service_Audio, "called. Revision {} Applet resource id {}", + AudioCore::GetRevisionNum(revision), applet_resource_user_id); -void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>(); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<IAudioRenderer>(system, params, audren_instance_count++); -} - -bool IsFeatureSupported(AudioFeatures feature, u32_le revision) { - // Byte swap - const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0'); - - switch (feature) { - case AudioFeatures::AudioUSBDeviceOutput: - return version_num >= 4U; - case AudioFeatures::Splitter: - return version_num >= 2U; - case AudioFeatures::PerformanceMetricsVersion2: - case AudioFeatures::VariadicCommandBuffer: - return version_num >= 5U; - default: - return false; - } + rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id, revision, + num_audio_devices++); } } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index 5922b4b27..4384a9b3c 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h @@ -1,9 +1,9 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "audio_core/audio_render_manager.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" @@ -16,6 +16,7 @@ class HLERequestContext; } namespace Service::Audio { +class IAudioRenderer; class AudRenU final : public ServiceFramework<AudRenU> { public: @@ -24,28 +25,14 @@ public: private: void OpenAudioRenderer(Kernel::HLERequestContext& ctx); - void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx); + void GetWorkBufferSize(Kernel::HLERequestContext& ctx); void GetAudioDeviceService(Kernel::HLERequestContext& ctx); void OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx); void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx); - void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx); - KernelHelpers::ServiceContext service_context; - - std::size_t audren_instance_count = 0; - Kernel::KEvent* buffer_event; + std::unique_ptr<AudioCore::AudioRenderer::Manager> impl; + u32 num_audio_devices{0}; }; -// Describes a particular audio feature that may be supported in a particular revision. -enum class AudioFeatures : u32 { - AudioUSBDeviceOutput, - Splitter, - PerformanceMetricsVersion2, - VariadicCommandBuffer, -}; - -// Tests if a particular audio feature is supported with a given audio revision. -bool IsFeatureSupported(AudioFeatures feature, u32_le revision); - } // namespace Service::Audio diff --git a/src/core/hle/service/audio/codecctl.cpp b/src/core/hle/service/audio/codecctl.cpp index 42961d908..81b956d7e 100644 --- a/src/core/hle/service/audio/codecctl.cpp +++ b/src/core/hle/service/audio/codecctl.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/audio/codecctl.h" diff --git a/src/core/hle/service/audio/codecctl.h b/src/core/hle/service/audio/codecctl.h index 58e53259e..34da98212 100644 --- a/src/core/hle/service/audio/codecctl.h +++ b/src/core/hle/service/audio/codecctl.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h index 6f8c09bcf..d706978cb 100644 --- a/src/core/hle/service/audio/errors.h +++ b/src/core/hle/service/audio/errors.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,8 +7,17 @@ namespace Service::Audio { -constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::Audio, 2}; -constexpr ResultCode ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8}; -constexpr ResultCode ERR_NOT_SUPPORTED{ErrorModule::Audio, 513}; +constexpr Result ERR_INVALID_DEVICE_NAME{ErrorModule::Audio, 1}; +constexpr Result ERR_OPERATION_FAILED{ErrorModule::Audio, 2}; +constexpr Result ERR_INVALID_SAMPLE_RATE{ErrorModule::Audio, 3}; +constexpr Result ERR_INSUFFICIENT_BUFFER_SIZE{ErrorModule::Audio, 4}; +constexpr Result ERR_MAXIMUM_SESSIONS_REACHED{ErrorModule::Audio, 5}; +constexpr Result ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8}; +constexpr Result ERR_INVALID_CHANNEL_COUNT{ErrorModule::Audio, 10}; +constexpr Result ERR_INVALID_UPDATE_DATA{ErrorModule::Audio, 41}; +constexpr Result ERR_POOL_MAPPING_FAILED{ErrorModule::Audio, 42}; +constexpr Result ERR_NOT_SUPPORTED{ErrorModule::Audio, 513}; +constexpr Result ERR_INVALID_PROCESS_HANDLE{ErrorModule::Audio, 1536}; +constexpr Result ERR_INVALID_REVISION{ErrorModule::Audio, 1537}; } // namespace Service::Audio diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 981b6c996..8bafc3a98 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <chrono> #include <cstring> @@ -256,6 +255,32 @@ void HwOpus::GetWorkBufferSizeEx(Kernel::HLERequestContext& ctx) { GetWorkBufferSize(ctx); } +void HwOpus::GetWorkBufferSizeForMultiStreamEx(Kernel::HLERequestContext& ctx) { + OpusMultiStreamParametersEx param; + std::memcpy(¶m, ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); + + const auto sample_rate = param.sample_rate; + const auto channel_count = param.channel_count; + const auto number_streams = param.number_streams; + const auto number_stereo_streams = param.number_stereo_streams; + + LOG_DEBUG( + Audio, + "called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}", + sample_rate, channel_count, number_streams, number_stereo_streams); + + ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || + sample_rate == 12000 || sample_rate == 8000, + "Invalid sample rate"); + + const u32 worker_buffer_sz = + static_cast<u32>(opus_multistream_decoder_get_size(number_streams, number_stereo_streams)); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(worker_buffer_sz); +} + void HwOpus::OpenHardwareOpusDecoder(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto sample_rate = rp.Pop<u32>(); @@ -299,7 +324,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx) { const auto sample_rate = rp.Pop<u32>(); const auto channel_count = rp.Pop<u32>(); - LOG_CRITICAL(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count); + LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count); ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || sample_rate == 12000 || sample_rate == 8000, @@ -336,7 +361,7 @@ HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} { {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"}, {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"}, {6, nullptr, "OpenHardwareOpusDecoderForMultiStreamEx"}, - {7, nullptr, "GetWorkBufferSizeForMultiStreamEx"}, + {7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h index b74824ff3..e6092e290 100644 --- a/src/core/hle/service/audio/hwopus.h +++ b/src/core/hle/service/audio/hwopus.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -12,6 +11,16 @@ class System; namespace Service::Audio { +struct OpusMultiStreamParametersEx { + u32 sample_rate; + u32 channel_count; + u32 number_streams; + u32 number_stereo_streams; + u32 use_large_frame_size; + u32 padding; + std::array<u32, 64> channel_mappings; +}; + class HwOpus final : public ServiceFramework<HwOpus> { public: explicit HwOpus(Core::System& system_); @@ -22,6 +31,7 @@ private: void OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx); void GetWorkBufferSize(Kernel::HLERequestContext& ctx); void GetWorkBufferSizeEx(Kernel::HLERequestContext& ctx); + void GetWorkBufferSizeForMultiStreamEx(Kernel::HLERequestContext& ctx); }; } // namespace Service::Audio diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp index ee49edbb9..cd0b405ff 100644 --- a/src/core/hle/service/bcat/backend/backend.cpp +++ b/src/core/hle/service/bcat/backend/backend.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/hex_util.h" #include "common/logging/log.h" @@ -75,7 +74,7 @@ void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) { SignalUpdate(); } -void ProgressServiceBackend::FinishDownload(ResultCode result) { +void ProgressServiceBackend::FinishDownload(Result result) { impl.total_downloaded_bytes = impl.total_bytes; impl.status = DeliveryCacheProgressImpl::Status::Done; impl.result = result; diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h index 63833c927..205ed0702 100644 --- a/src/core/hle/service/bcat/backend/backend.h +++ b/src/core/hle/service/bcat/backend/backend.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -50,7 +49,7 @@ struct DeliveryCacheProgressImpl { }; Status status; - ResultCode result = ResultSuccess; + Result result = ResultSuccess; DirectoryName current_directory; FileName current_file; s64 current_downloaded_bytes; ///< Bytes downloaded on current file. @@ -91,7 +90,7 @@ public: void CommitDirectory(std::string_view dir_name); // Notifies the application that the operation completed with result code result. - void FinishDownload(ResultCode result); + void FinishDownload(Result result); private: explicit ProgressServiceBackend(Core::System& system, std::string_view event_name); diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp index 5a95707de..d0ac17324 100644 --- a/src/core/hle/service/bcat/bcat.cpp +++ b/src/core/hle/service/bcat/bcat.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/bcat/bcat.h" diff --git a/src/core/hle/service/bcat/bcat.h b/src/core/hle/service/bcat/bcat.h index 1eba477da..db9d3c8c5 100644 --- a/src/core/hle/service/bcat/bcat.h +++ b/src/core/hle/service/bcat/bcat.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp index 500e7e52d..bc08ac487 100644 --- a/src/core/hle/service/bcat/bcat_module.cpp +++ b/src/core/hle/service/bcat/bcat_module.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cctype> #include <mbedtls/md5.h> @@ -19,15 +18,15 @@ namespace Service::BCAT { -constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1}; -constexpr ResultCode ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2}; -constexpr ResultCode ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6}; -constexpr ResultCode ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7}; +constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1}; +constexpr Result ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2}; +constexpr Result ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6}; +constexpr Result ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7}; // The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the files // and if any of them have a non-zero result it just forwards that result. This is the FS error code // for permission denied, which is the closest approximation of this scenario. -constexpr ResultCode ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400}; +constexpr Result ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400}; using BCATDigest = std::array<u8, 0x10>; @@ -141,8 +140,8 @@ public: {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"}, {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"}, {30100, &IBcatService::SetPassphrase, "SetPassphrase"}, - {30101, nullptr, "Unknown"}, - {30102, nullptr, "Unknown2"}, + {30101, nullptr, "Unknown30101"}, + {30102, nullptr, "Unknown30102"}, {30200, nullptr, "RegisterBackgroundDeliveryTask"}, {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, {30202, nullptr, "BlockDeliveryTask"}, diff --git a/src/core/hle/service/bcat/bcat_module.h b/src/core/hle/service/bcat/bcat_module.h index 738731c06..b2fcf9bfb 100644 --- a/src/core/hle/service/bcat/bcat_module.h +++ b/src/core/hle/service/bcat/bcat_module.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/bpc/bpc.cpp b/src/core/hle/service/bpc/bpc.cpp index 78e01d8d8..466163538 100644 --- a/src/core/hle/service/bpc/bpc.cpp +++ b/src/core/hle/service/bpc/bpc.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/bpc/bpc.h b/src/core/hle/service/bpc/bpc.h index 6ec25aa9b..8adc2f962 100644 --- a/src/core/hle/service/bpc/bpc.h +++ b/src/core/hle/service/bpc/bpc.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp index 0787f43f4..ec7e5320c 100644 --- a/src/core/hle/service/btdrv/btdrv.cpp +++ b/src/core/hle/service/btdrv/btdrv.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/core.h" @@ -182,6 +181,11 @@ public: {147, nullptr, "RegisterAudioControlNotification"}, {148, nullptr, "SendAudioControlPassthroughCommand"}, {149, nullptr, "SendAudioControlSetAbsoluteVolumeCommand"}, + {150, nullptr, "AcquireAudioSinkVolumeLocallyChangedEvent"}, + {151, nullptr, "AcquireAudioSinkVolumeUpdateRequestCompletedEvent"}, + {152, nullptr, "GetAudioSinkVolume"}, + {153, nullptr, "RequestUpdateAudioSinkVolume"}, + {154, nullptr, "IsAudioSinkVolumeSupported"}, {256, nullptr, "IsManufacturingMode"}, {257, nullptr, "EmulateBluetoothCrash"}, {258, nullptr, "GetBleChannelMap"}, diff --git a/src/core/hle/service/btdrv/btdrv.h b/src/core/hle/service/btdrv/btdrv.h index 191410dbc..9cbe2926f 100644 --- a/src/core/hle/service/btdrv/btdrv.h +++ b/src/core/hle/service/btdrv/btdrv.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index cc268d877..eebf85e03 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> @@ -215,8 +214,12 @@ public: {76, nullptr, "Unknown76"}, {100, nullptr, "Unknown100"}, {101, nullptr, "Unknown101"}, - {110, nullptr, "Unknown102"}, - {111, nullptr, "Unknown103"}, + {110, nullptr, "Unknown110"}, + {111, nullptr, "Unknown111"}, + {112, nullptr, "Unknown112"}, + {113, nullptr, "Unknown113"}, + {114, nullptr, "Unknown114"}, + {115, nullptr, "Unknown115"}, }; // clang-format on diff --git a/src/core/hle/service/btm/btm.h b/src/core/hle/service/btm/btm.h index c6b878043..9dcda1848 100644 --- a/src/core/hle/service/btm/btm.h +++ b/src/core/hle/service/btm/btm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp index 5b7fe8e9b..13940a8c9 100644 --- a/src/core/hle/service/caps/caps.cpp +++ b/src/core/hle/service/caps/caps.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/caps/caps.h" #include "core/hle/service/caps/caps_a.h" diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h index 7254055e6..3e89c82cb 100644 --- a/src/core/hle/service/caps/caps.h +++ b/src/core/hle/service/caps/caps.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp index 6220e9f77..44267b284 100644 --- a/src/core/hle/service/caps/caps_a.cpp +++ b/src/core/hle/service/caps/caps_a.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/caps/caps_a.h" diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h index 389cc6dbe..319c173d8 100644 --- a/src/core/hle/service/caps/caps_a.h +++ b/src/core/hle/service/caps/caps_a.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp index a9ad5c8b1..725a2e3a7 100644 --- a/src/core/hle/service/caps/caps_c.cpp +++ b/src/core/hle/service/caps/caps_c.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h index c6d1dfdce..983a4212d 100644 --- a/src/core/hle/service/caps/caps_c.h +++ b/src/core/hle/service/caps/caps_c.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/caps/caps_sc.cpp b/src/core/hle/service/caps/caps_sc.cpp index d91e18e80..395b13da7 100644 --- a/src/core/hle/service/caps/caps_sc.cpp +++ b/src/core/hle/service/caps/caps_sc.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/caps/caps_sc.h" diff --git a/src/core/hle/service/caps/caps_sc.h b/src/core/hle/service/caps/caps_sc.h index e79a33ee5..e5600f6d7 100644 --- a/src/core/hle/service/caps/caps_sc.h +++ b/src/core/hle/service/caps/caps_sc.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp index 33a976ddf..62b9edd41 100644 --- a/src/core/hle/service/caps/caps_ss.cpp +++ b/src/core/hle/service/caps/caps_ss.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/caps/caps_ss.h" diff --git a/src/core/hle/service/caps/caps_ss.h b/src/core/hle/service/caps/caps_ss.h index 1816f7885..718ade485 100644 --- a/src/core/hle/service/caps/caps_ss.h +++ b/src/core/hle/service/caps/caps_ss.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp index 45b705950..fcb496756 100644 --- a/src/core/hle/service/caps/caps_su.cpp +++ b/src/core/hle/service/caps/caps_su.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h index b366fdb13..c9a1d507b 100644 --- a/src/core/hle/service/caps/caps_su.h +++ b/src/core/hle/service/caps/caps_su.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp index 8f8ee2bb4..5fbba8673 100644 --- a/src/core/hle/service/caps/caps_u.cpp +++ b/src/core/hle/service/caps/caps_u.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h index e7e0d8775..c3d4b9cea 100644 --- a/src/core/hle/service/caps/caps_u.h +++ b/src/core/hle/service/caps/caps_u.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/erpt/erpt.cpp b/src/core/hle/service/erpt/erpt.cpp index c767926a4..923c0022a 100644 --- a/src/core/hle/service/erpt/erpt.cpp +++ b/src/core/hle/service/erpt/erpt.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/erpt/erpt.h b/src/core/hle/service/erpt/erpt.h index 8cd5c081f..507d626ec 100644 --- a/src/core/hle/service/erpt/erpt.h +++ b/src/core/hle/service/erpt/erpt.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index f6184acc9..ff9b0427c 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/crypto/key_manager.h" #include "core/hle/ipc_helpers.h" @@ -9,8 +8,8 @@ namespace Service::ES { -constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::ETicket, 2}; -constexpr ResultCode ERROR_INVALID_RIGHTS_ID{ErrorModule::ETicket, 3}; +constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::ETicket, 2}; +constexpr Result ERROR_INVALID_RIGHTS_ID{ErrorModule::ETicket, 3}; class ETicket final : public ServiceFramework<ETicket> { public: diff --git a/src/core/hle/service/es/es.h b/src/core/hle/service/es/es.h index 2a7b27d12..530563550 100644 --- a/src/core/hle/service/es/es.h +++ b/src/core/hle/service/es/es.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/eupld/eupld.cpp b/src/core/hle/service/eupld/eupld.cpp index 2d650b1b7..d1553ace0 100644 --- a/src/core/hle/service/eupld/eupld.cpp +++ b/src/core/hle/service/eupld/eupld.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/eupld/eupld.h b/src/core/hle/service/eupld/eupld.h index 539993a9d..5de8219be 100644 --- a/src/core/hle/service/eupld/eupld.h +++ b/src/core/hle/service/eupld/eupld.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index f84506af0..27675615b 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include <cstring> @@ -63,8 +62,7 @@ enum class FatalType : u32 { ErrorScreen = 2, }; -static void GenerateErrorReport(Core::System& system, ResultCode error_code, - const FatalInfo& info) { +static void GenerateErrorReport(Core::System& system, Result error_code, const FatalInfo& info) { const auto title_id = system.GetCurrentProcessProgramID(); std::string crash_report = fmt::format( "Yuzu {}-{} crash report\n" @@ -107,7 +105,7 @@ static void GenerateErrorReport(Core::System& system, ResultCode error_code, info.backtrace_size, info.ArchAsString(), info.unk10); } -static void ThrowFatalError(Core::System& system, ResultCode error_code, FatalType fatal_type, +static void ThrowFatalError(Core::System& system, Result error_code, FatalType fatal_type, const FatalInfo& info) { LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}", fatal_type, error_code.raw); @@ -130,7 +128,7 @@ static void ThrowFatalError(Core::System& system, ResultCode error_code, FatalTy void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) { LOG_ERROR(Service_Fatal, "called"); IPC::RequestParser rp{ctx}; - const auto error_code = rp.Pop<ResultCode>(); + const auto error_code = rp.Pop<Result>(); ThrowFatalError(system, error_code, FatalType::ErrorScreen, {}); IPC::ResponseBuilder rb{ctx, 2}; @@ -140,7 +138,7 @@ void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) { void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { LOG_ERROR(Service_Fatal, "called"); IPC::RequestParser rp(ctx); - const auto error_code = rp.Pop<ResultCode>(); + const auto error_code = rp.Pop<Result>(); const auto fatal_type = rp.PopEnum<FatalType>(); ThrowFatalError(system, error_code, fatal_type, @@ -152,7 +150,7 @@ void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) { LOG_ERROR(Service_Fatal, "called"); IPC::RequestParser rp(ctx); - const auto error_code = rp.Pop<ResultCode>(); + const auto error_code = rp.Pop<Result>(); const auto fatal_type = rp.PopEnum<FatalType>(); const auto fatal_info = ctx.ReadBuffer(); FatalInfo info{}; diff --git a/src/core/hle/service/fatal/fatal.h b/src/core/hle/service/fatal/fatal.h index 2095bf89f..a7a310f7b 100644 --- a/src/core/hle/service/fatal/fatal.h +++ b/src/core/hle/service/fatal/fatal.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/fatal/fatal_p.cpp b/src/core/hle/service/fatal/fatal_p.cpp index 8672b85dc..4a81bb5e2 100644 --- a/src/core/hle/service/fatal/fatal_p.cpp +++ b/src/core/hle/service/fatal/fatal_p.cpp @@ -1,13 +1,18 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/fatal/fatal_p.h" namespace Service::Fatal { Fatal_P::Fatal_P(std::shared_ptr<Module> module_, Core::System& system_) - : Interface(std::move(module_), system_, "fatal:p") {} + : Interface(std::move(module_), system_, "fatal:p") { + static const FunctionInfo functions[] = { + {0, nullptr, "GetFatalEvent"}, + {10, nullptr, "GetFatalContext"}, + }; + RegisterHandlers(functions); +} Fatal_P::~Fatal_P() = default; diff --git a/src/core/hle/service/fatal/fatal_p.h b/src/core/hle/service/fatal/fatal_p.h index ffa5b7b98..f74336835 100644 --- a/src/core/hle/service/fatal/fatal_p.h +++ b/src/core/hle/service/fatal/fatal_p.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/fatal/fatal_u.cpp b/src/core/hle/service/fatal/fatal_u.cpp index 82993938a..3739711fc 100644 --- a/src/core/hle/service/fatal/fatal_u.cpp +++ b/src/core/hle/service/fatal/fatal_u.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/fatal/fatal_u.h" diff --git a/src/core/hle/service/fatal/fatal_u.h b/src/core/hle/service/fatal/fatal_u.h index 0b58c9112..65fbe2696 100644 --- a/src/core/hle/service/fatal/fatal_u.h +++ b/src/core/hle/service/fatal/fatal_u.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/fgm/fgm.cpp b/src/core/hle/service/fgm/fgm.cpp index d7a638f96..7e9fb0385 100644 --- a/src/core/hle/service/fgm/fgm.cpp +++ b/src/core/hle/service/fgm/fgm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/fgm/fgm.h b/src/core/hle/service/fgm/fgm.h index 75978f2ed..077e48812 100644 --- a/src/core/hle/service/fgm/fgm.h +++ b/src/core/hle/service/fgm/fgm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 3703ca4c6..11c604a0f 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <utility> @@ -50,7 +49,7 @@ std::string VfsDirectoryServiceWrapper::GetName() const { return backing->GetName(); } -ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const { +Result VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const { std::string path(Common::FS::SanitizePath(path_)); auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); if (dir == nullptr) { @@ -74,7 +73,7 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 return ResultSuccess; } -ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { +Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { std::string path(Common::FS::SanitizePath(path_)); if (path.empty()) { // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but... @@ -93,7 +92,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons return ResultSuccess; } -ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const { +Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const { std::string path(Common::FS::SanitizePath(path_)); // NOTE: This is inaccurate behavior. CreateDirectory is not recursive. @@ -117,7 +116,7 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) return ResultSuccess; } -ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const { +Result VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const { std::string path(Common::FS::SanitizePath(path_)); auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) { @@ -127,7 +126,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) return ResultSuccess; } -ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const { +Result VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const { std::string path(Common::FS::SanitizePath(path_)); auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) { @@ -137,7 +136,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str return ResultSuccess; } -ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const { +Result VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const { const std::string sanitized_path(Common::FS::SanitizePath(path)); auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path)); @@ -149,8 +148,8 @@ ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::stri return ResultSuccess; } -ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, - const std::string& dest_path_) const { +Result VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, + const std::string& dest_path_) const { std::string src_path(Common::FS::SanitizePath(src_path_)); std::string dest_path(Common::FS::SanitizePath(dest_path_)); auto src = backing->GetFileRelative(src_path); @@ -174,7 +173,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found."); ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), - "Could not write all of the bytes but everything else has succeded."); + "Could not write all of the bytes but everything else has succeeded."); if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) { // TODO(DarkLordZach): Find a better error code for this @@ -184,8 +183,8 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, return ResultSuccess; } -ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_, - const std::string& dest_path_) const { +Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_, + const std::string& dest_path_) const { std::string src_path(Common::FS::SanitizePath(src_path_)); std::string dest_path(Common::FS::SanitizePath(dest_path_)); auto src = GetDirectoryRelativeWrapped(backing, src_path); @@ -274,28 +273,27 @@ FileSystemController::FileSystemController(Core::System& system_) : system{syste FileSystemController::~FileSystemController() = default; -ResultCode FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) { +Result FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) { romfs_factory = std::move(factory); LOG_DEBUG(Service_FS, "Registered RomFS"); return ResultSuccess; } -ResultCode FileSystemController::RegisterSaveData( - std::unique_ptr<FileSys::SaveDataFactory>&& factory) { +Result FileSystemController::RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) { ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data"); save_data_factory = std::move(factory); LOG_DEBUG(Service_FS, "Registered save data"); return ResultSuccess; } -ResultCode FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) { +Result FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) { ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC"); sdmc_factory = std::move(factory); LOG_DEBUG(Service_FS, "Registered SDMC"); return ResultSuccess; } -ResultCode FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) { +Result FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) { ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS"); bis_factory = std::move(factory); LOG_DEBUG(Service_FS, "Registered BIS"); diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index b155e0811..5b27de9fa 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -59,10 +58,10 @@ public: explicit FileSystemController(Core::System& system_); ~FileSystemController(); - ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); - ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory); - ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); - ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); + Result RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); + Result RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory); + Result RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); + Result RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); void SetPackedUpdate(FileSys::VirtualFile update_raw); ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() const; @@ -142,7 +141,7 @@ private: void InstallInterfaces(Core::System& system); -// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of +// A class that wraps a VfsDirectory with methods that return ResultVal and Result instead of // pointers and booleans. This makes using a VfsDirectory with switch services much easier and // avoids repetitive code. class VfsDirectoryServiceWrapper { @@ -161,35 +160,35 @@ public: * @param size The size of the new file, filled with zeroes * @return Result of the operation */ - ResultCode CreateFile(const std::string& path, u64 size) const; + Result CreateFile(const std::string& path, u64 size) const; /** * Delete a file specified by its path * @param path Path relative to the archive * @return Result of the operation */ - ResultCode DeleteFile(const std::string& path) const; + Result DeleteFile(const std::string& path) const; /** * Create a directory specified by its path * @param path Path relative to the archive * @return Result of the operation */ - ResultCode CreateDirectory(const std::string& path) const; + Result CreateDirectory(const std::string& path) const; /** * Delete a directory specified by its path * @param path Path relative to the archive * @return Result of the operation */ - ResultCode DeleteDirectory(const std::string& path) const; + Result DeleteDirectory(const std::string& path) const; /** * Delete a directory specified by its path and anything under it * @param path Path relative to the archive * @return Result of the operation */ - ResultCode DeleteDirectoryRecursively(const std::string& path) const; + Result DeleteDirectoryRecursively(const std::string& path) const; /** * Cleans the specified directory. This is similar to DeleteDirectoryRecursively, @@ -201,7 +200,7 @@ public: * * @return Result of the operation. */ - ResultCode CleanDirectoryRecursively(const std::string& path) const; + Result CleanDirectoryRecursively(const std::string& path) const; /** * Rename a File specified by its path @@ -209,7 +208,7 @@ public: * @param dest_path Destination path relative to the archive * @return Result of the operation */ - ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const; + Result RenameFile(const std::string& src_path, const std::string& dest_path) const; /** * Rename a Directory specified by its path @@ -217,7 +216,7 @@ public: * @param dest_path Destination path relative to the archive * @return Result of the operation */ - ResultCode RenameDirectory(const std::string& src_path, const std::string& dest_path) const; + Result RenameDirectory(const std::string& src_path, const std::string& dest_path) const; /** * Open a file specified by its path, using the specified mode diff --git a/src/core/hle/service/filesystem/fsp_ldr.cpp b/src/core/hle/service/filesystem/fsp_ldr.cpp index f112ae9d0..1e3366e71 100644 --- a/src/core/hle/service/filesystem/fsp_ldr.cpp +++ b/src/core/hle/service/filesystem/fsp_ldr.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/filesystem/fsp_ldr.h" diff --git a/src/core/hle/service/filesystem/fsp_ldr.h b/src/core/hle/service/filesystem/fsp_ldr.h index d6432a0e1..358739a87 100644 --- a/src/core/hle/service/filesystem/fsp_ldr.h +++ b/src/core/hle/service/filesystem/fsp_ldr.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/filesystem/fsp_pr.cpp b/src/core/hle/service/filesystem/fsp_pr.cpp index 9b7f7d861..4ffc31977 100644 --- a/src/core/hle/service/filesystem/fsp_pr.cpp +++ b/src/core/hle/service/filesystem/fsp_pr.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/filesystem/fsp_pr.h" diff --git a/src/core/hle/service/filesystem/fsp_pr.h b/src/core/hle/service/filesystem/fsp_pr.h index 9e622518c..bd4e0a730 100644 --- a/src/core/hle/service/filesystem/fsp_pr.h +++ b/src/core/hle/service/filesystem/fsp_pr.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index b087e7bba..e23eae36a 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cinttypes> #include <cstring> @@ -58,7 +57,8 @@ enum class FileSystemType : u8 { class IStorage final : public ServiceFramework<IStorage> { public: explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_) - : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) { + : ServiceFramework{system_, "IStorage", ServiceThreadType::CreateNew}, + backend(std::move(backend_)) { static const FunctionInfo functions[] = { {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, @@ -116,7 +116,8 @@ private: class IFile final : public ServiceFramework<IFile> { public: explicit IFile(Core::System& system_, FileSys::VirtualFile backend_) - : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) { + : ServiceFramework{system_, "IFile", ServiceThreadType::CreateNew}, + backend(std::move(backend_)) { static const FunctionInfo functions[] = { {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, @@ -245,14 +246,16 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec entries.reserve(entries.size() + new_data.size()); for (const auto& new_entry : new_data) { - entries.emplace_back(new_entry->GetName(), type, new_entry->GetSize()); + entries.emplace_back(new_entry->GetName(), type, + type == FileSys::EntryType::Directory ? 0 : new_entry->GetSize()); } } class IDirectory final : public ServiceFramework<IDirectory> { public: explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_) - : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) { + : ServiceFramework{system_, "IDirectory", ServiceThreadType::CreateNew}, + backend(std::move(backend_)) { static const FunctionInfo functions[] = { {0, &IDirectory::Read, "Read"}, {1, &IDirectory::GetEntryCount, "GetEntryCount"}, @@ -308,8 +311,8 @@ private: class IFileSystem final : public ServiceFramework<IFileSystem> { public: explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_) - : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move( - size_)} { + : ServiceFramework{system_, "IFileSystem", ServiceThreadType::CreateNew}, + backend{std::move(backend_)}, size{std::move(size_)} { static const FunctionInfo functions[] = { {0, &IFileSystem::CreateFile, "CreateFile"}, {1, &IFileSystem::DeleteFile, "DeleteFile"}, @@ -897,7 +900,7 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { case FileSys::SaveDataSpaceId::TemporaryStorage: case FileSys::SaveDataSpaceId::ProperSystem: case FileSys::SaveDataSpaceId::SafeMode: - UNREACHABLE(); + ASSERT(false); } auto filesystem = std::make_shared<IFileSystem>(system, std::move(dir.Unwrap()), diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index 556708284..36f552e05 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/friend/errors.h b/src/core/hle/service/friend/errors.h index b3996e275..ff525d865 100644 --- a/src/core/hle/service/friend/errors.h +++ b/src/core/hle/service/friend/errors.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,5 +7,5 @@ namespace Service::Friend { -constexpr ResultCode ERR_NO_NOTIFICATIONS{ErrorModule::Account, 15}; +constexpr Result ERR_NO_NOTIFICATIONS{ErrorModule::Account, 15}; } diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 79cd3acbb..e0db787fc 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <queue> #include "common/logging/log.h" diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h index 8be3321db..444da8b35 100644 --- a/src/core/hle/service/friend/friend.h +++ b/src/core/hle/service/friend/friend.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/friend/friend_interface.cpp b/src/core/hle/service/friend/friend_interface.cpp index 9b18b2a32..c8b98b1f0 100644 --- a/src/core/hle/service/friend/friend_interface.cpp +++ b/src/core/hle/service/friend/friend_interface.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/friend/friend_interface.h" diff --git a/src/core/hle/service/friend/friend_interface.h b/src/core/hle/service/friend/friend_interface.h index 43d914b32..3a2184c34 100644 --- a/src/core/hle/service/friend/friend_interface.h +++ b/src/core/hle/service/friend/friend_interface.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp index 2feead2aa..49b6d45fe 100644 --- a/src/core/hle/service/glue/arp.cpp +++ b/src/core/hle/service/glue/arp.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> @@ -154,7 +153,7 @@ class IRegistrar final : public ServiceFramework<IRegistrar> { friend class ARP_W; public: - using IssuerFn = std::function<ResultCode(u64, ApplicationLaunchProperty, std::vector<u8>)>; + using IssuerFn = std::function<Result(u64, ApplicationLaunchProperty, std::vector<u8>)>; explicit IRegistrar(Core::System& system_, IssuerFn&& issuer) : ServiceFramework{system_, "IRegistrar"}, issue_process_id{std::move(issuer)} { diff --git a/src/core/hle/service/glue/arp.h b/src/core/hle/service/glue/arp.h index 0df3c5e1f..06c992e88 100644 --- a/src/core/hle/service/glue/arp.h +++ b/src/core/hle/service/glue/arp.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/glue/bgtc.cpp b/src/core/hle/service/glue/bgtc.cpp index 564c3b750..3248091c3 100644 --- a/src/core/hle/service/glue/bgtc.cpp +++ b/src/core/hle/service/glue/bgtc.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/core.h" diff --git a/src/core/hle/service/glue/bgtc.h b/src/core/hle/service/glue/bgtc.h index 4c0142fd5..d6e2baec1 100644 --- a/src/core/hle/service/glue/bgtc.h +++ b/src/core/hle/service/glue/bgtc.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/glue/ectx.cpp b/src/core/hle/service/glue/ectx.cpp index 249c6f003..1bd9314ae 100644 --- a/src/core/hle/service/glue/ectx.cpp +++ b/src/core/hle/service/glue/ectx.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/glue/ectx.h" diff --git a/src/core/hle/service/glue/ectx.h b/src/core/hle/service/glue/ectx.h index b275e808a..a608de053 100644 --- a/src/core/hle/service/glue/ectx.h +++ b/src/core/hle/service/glue/ectx.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/glue/errors.h b/src/core/hle/service/glue/errors.h index f6647f724..d4ce7f44e 100644 --- a/src/core/hle/service/glue/errors.h +++ b/src/core/hle/service/glue/errors.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,9 +7,9 @@ namespace Service::Glue { -constexpr ResultCode ERR_INVALID_RESOURCE{ErrorModule::ARP, 30}; -constexpr ResultCode ERR_INVALID_PROCESS_ID{ErrorModule::ARP, 31}; -constexpr ResultCode ERR_INVALID_ACCESS{ErrorModule::ARP, 42}; -constexpr ResultCode ERR_NOT_REGISTERED{ErrorModule::ARP, 102}; +constexpr Result ERR_INVALID_RESOURCE{ErrorModule::ARP, 30}; +constexpr Result ERR_INVALID_PROCESS_ID{ErrorModule::ARP, 31}; +constexpr Result ERR_INVALID_ACCESS{ErrorModule::ARP, 42}; +constexpr Result ERR_NOT_REGISTERED{ErrorModule::ARP, 102}; } // namespace Service::Glue diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp index b24d469cf..717f2562b 100644 --- a/src/core/hle/service/glue/glue.cpp +++ b/src/core/hle/service/glue/glue.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> #include "core/core.h" diff --git a/src/core/hle/service/glue/glue.h b/src/core/hle/service/glue/glue.h index 112cd238b..ae7c6d235 100644 --- a/src/core/hle/service/glue/glue.h +++ b/src/core/hle/service/glue/glue.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/glue/glue_manager.cpp b/src/core/hle/service/glue/glue_manager.cpp index 48e133b48..8a654cdca 100644 --- a/src/core/hle/service/glue/glue_manager.cpp +++ b/src/core/hle/service/glue/glue_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/glue/errors.h" #include "core/hle/service/glue/glue_manager.h" @@ -42,8 +41,8 @@ ResultVal<std::vector<u8>> ARPManager::GetControlProperty(u64 title_id) const { return iter->second.control; } -ResultCode ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch, - std::vector<u8> control) { +Result ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch, + std::vector<u8> control) { if (title_id == 0) { return ERR_INVALID_PROCESS_ID; } @@ -57,7 +56,7 @@ ResultCode ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch, return ResultSuccess; } -ResultCode ARPManager::Unregister(u64 title_id) { +Result ARPManager::Unregister(u64 title_id) { if (title_id == 0) { return ERR_INVALID_PROCESS_ID; } diff --git a/src/core/hle/service/glue/glue_manager.h b/src/core/hle/service/glue/glue_manager.h index 4bc5297c6..cd0b092ac 100644 --- a/src/core/hle/service/glue/glue_manager.h +++ b/src/core/hle/service/glue/glue_manager.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -43,12 +42,12 @@ public: // Adds a new entry to the internal database with the provided parameters, returning // ERR_INVALID_ACCESS if attempting to re-register a title ID without an intermediate Unregister // step, and ERR_INVALID_PROCESS_ID if the title ID is 0. - ResultCode Register(u64 title_id, ApplicationLaunchProperty launch, std::vector<u8> control); + Result Register(u64 title_id, ApplicationLaunchProperty launch, std::vector<u8> control); // Removes the registration for the provided title ID from the database, returning // ERR_NOT_REGISTERED if it doesn't exist in the database and ERR_INVALID_PROCESS_ID if the // title ID is 0. - ResultCode Unregister(u64 title_id); + Result Unregister(u64 title_id); // Removes all entries from the database, always succeeds. Should only be used when resetting // system state. diff --git a/src/core/hle/service/glue/notif.cpp b/src/core/hle/service/glue/notif.cpp index c559ec9df..3ace2dabd 100644 --- a/src/core/hle/service/glue/notif.cpp +++ b/src/core/hle/service/glue/notif.cpp @@ -1,7 +1,11 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#include <algorithm> +#include <cstring> + +#include "common/assert.h" +#include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/glue/notif.h" @@ -10,11 +14,11 @@ namespace Service::Glue { NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} { // clang-format off static const FunctionInfo functions[] = { - {500, nullptr, "RegisterAlarmSetting"}, - {510, nullptr, "UpdateAlarmSetting"}, + {500, &NOTIF_A::RegisterAlarmSetting, "RegisterAlarmSetting"}, + {510, &NOTIF_A::UpdateAlarmSetting, "UpdateAlarmSetting"}, {520, &NOTIF_A::ListAlarmSettings, "ListAlarmSettings"}, - {530, nullptr, "LoadApplicationParameter"}, - {540, nullptr, "DeleteAlarmSetting"}, + {530, &NOTIF_A::LoadApplicationParameter, "LoadApplicationParameter"}, + {540, &NOTIF_A::DeleteAlarmSetting, "DeleteAlarmSetting"}, {1000, &NOTIF_A::Initialize, "Initialize"}, }; // clang-format on @@ -24,21 +28,132 @@ NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} { NOTIF_A::~NOTIF_A() = default; +void NOTIF_A::RegisterAlarmSetting(Kernel::HLERequestContext& ctx) { + const auto alarm_setting_buffer_size = ctx.GetReadBufferSize(0); + const auto application_parameter_size = ctx.GetReadBufferSize(1); + + ASSERT_MSG(alarm_setting_buffer_size == sizeof(AlarmSetting), + "alarm_setting_buffer_size is not 0x40 bytes"); + ASSERT_MSG(application_parameter_size <= sizeof(ApplicationParameter), + "application_parameter_size is bigger than 0x400 bytes"); + + AlarmSetting new_alarm{}; + memcpy(&new_alarm, ctx.ReadBuffer(0).data(), sizeof(AlarmSetting)); + + // TODO: Count alarms per game id + if (alarms.size() >= max_alarms) { + LOG_ERROR(Service_NOTIF, "Alarm limit reached"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + new_alarm.alarm_setting_id = last_alarm_setting_id++; + alarms.push_back(new_alarm); + + // TODO: Save application parameter data + + LOG_WARNING(Service_NOTIF, + "(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}", + application_parameter_size, new_alarm.alarm_setting_id, new_alarm.kind, + new_alarm.muted); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + rb.Push(new_alarm.alarm_setting_id); +} + +void NOTIF_A::UpdateAlarmSetting(Kernel::HLERequestContext& ctx) { + const auto alarm_setting_buffer_size = ctx.GetReadBufferSize(0); + const auto application_parameter_size = ctx.GetReadBufferSize(1); + + ASSERT_MSG(alarm_setting_buffer_size == sizeof(AlarmSetting), + "alarm_setting_buffer_size is not 0x40 bytes"); + ASSERT_MSG(application_parameter_size <= sizeof(ApplicationParameter), + "application_parameter_size is bigger than 0x400 bytes"); + + AlarmSetting alarm_setting{}; + memcpy(&alarm_setting, ctx.ReadBuffer(0).data(), sizeof(AlarmSetting)); + + const auto alarm_it = GetAlarmFromId(alarm_setting.alarm_setting_id); + if (alarm_it != alarms.end()) { + LOG_DEBUG(Service_NOTIF, "Alarm updated"); + *alarm_it = alarm_setting; + // TODO: Save application parameter data + } + + LOG_WARNING(Service_NOTIF, + "(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}", + application_parameter_size, alarm_setting.alarm_setting_id, alarm_setting.kind, + alarm_setting.muted); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void NOTIF_A::ListAlarmSettings(Kernel::HLERequestContext& ctx) { - // Returns an array of AlarmSetting - constexpr s32 alarm_count = 0; + LOG_INFO(Service_NOTIF, "called, alarm_count={}", alarms.size()); - LOG_WARNING(Service_NOTIF, "(STUBBED) called"); + // TODO: Only return alarms of this game id + ctx.WriteBuffer(alarms); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(alarm_count); + rb.Push(static_cast<u32>(alarms.size())); +} + +void NOTIF_A::LoadApplicationParameter(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto alarm_setting_id{rp.Pop<AlarmSettingId>()}; + + const auto alarm_it = GetAlarmFromId(alarm_setting_id); + if (alarm_it == alarms.end()) { + LOG_ERROR(Service_NOTIF, "Invalid alarm setting id={}", alarm_setting_id); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + // TODO: Read application parameter related to this setting id + ApplicationParameter application_parameter{}; + + LOG_WARNING(Service_NOTIF, "(STUBBED) called, alarm_setting_id={}", alarm_setting_id); + + ctx.WriteBuffer(application_parameter); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + rb.Push(static_cast<u32>(application_parameter.size())); +} + +void NOTIF_A::DeleteAlarmSetting(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto alarm_setting_id{rp.Pop<AlarmSettingId>()}; + + std::erase_if(alarms, [alarm_setting_id](const AlarmSetting& alarm) { + return alarm.alarm_setting_id == alarm_setting_id; + }); + + LOG_INFO(Service_NOTIF, "called, alarm_setting_id={}", alarm_setting_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); } void NOTIF_A::Initialize(Kernel::HLERequestContext& ctx) { + // TODO: Load previous alarms from config + LOG_WARNING(Service_NOTIF, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } +std::vector<NOTIF_A::AlarmSetting>::iterator NOTIF_A::GetAlarmFromId( + AlarmSettingId alarm_setting_id) { + return std::find_if(alarms.begin(), alarms.end(), + [alarm_setting_id](const AlarmSetting& alarm) { + return alarm.alarm_setting_id == alarm_setting_id; + }); +} + } // namespace Service::Glue diff --git a/src/core/hle/service/glue/notif.h b/src/core/hle/service/glue/notif.h index 6ecf2015c..4467e1f35 100644 --- a/src/core/hle/service/glue/notif.h +++ b/src/core/hle/service/glue/notif.h @@ -1,9 +1,12 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include <array> +#include <vector> + +#include "common/uuid.h" #include "core/hle/service/service.h" namespace Core { @@ -18,8 +21,52 @@ public: ~NOTIF_A() override; private: + static constexpr std::size_t max_alarms = 8; + + // This is nn::notification::AlarmSettingId + using AlarmSettingId = u16; + static_assert(sizeof(AlarmSettingId) == 0x2, "AlarmSettingId is an invalid size"); + + using ApplicationParameter = std::array<u8, 0x400>; + static_assert(sizeof(ApplicationParameter) == 0x400, "ApplicationParameter is an invalid size"); + + struct DailyAlarmSetting { + s8 hour; + s8 minute; + }; + static_assert(sizeof(DailyAlarmSetting) == 0x2, "DailyAlarmSetting is an invalid size"); + + struct WeeklyScheduleAlarmSetting { + INSERT_PADDING_BYTES(0xA); + std::array<DailyAlarmSetting, 0x7> day_of_week; + }; + static_assert(sizeof(WeeklyScheduleAlarmSetting) == 0x18, + "WeeklyScheduleAlarmSetting is an invalid size"); + + // This is nn::notification::AlarmSetting + struct AlarmSetting { + AlarmSettingId alarm_setting_id; + u8 kind; + u8 muted; + INSERT_PADDING_BYTES(0x4); + Common::UUID account_id; + u64 application_id; + INSERT_PADDING_BYTES(0x8); + WeeklyScheduleAlarmSetting schedule; + }; + static_assert(sizeof(AlarmSetting) == 0x40, "AlarmSetting is an invalid size"); + + void RegisterAlarmSetting(Kernel::HLERequestContext& ctx); + void UpdateAlarmSetting(Kernel::HLERequestContext& ctx); void ListAlarmSettings(Kernel::HLERequestContext& ctx); + void LoadApplicationParameter(Kernel::HLERequestContext& ctx); + void DeleteAlarmSetting(Kernel::HLERequestContext& ctx); void Initialize(Kernel::HLERequestContext& ctx); + + std::vector<AlarmSetting>::iterator GetAlarmFromId(AlarmSettingId alarm_setting_id); + + std::vector<AlarmSetting> alarms{}; + AlarmSettingId last_alarm_setting_id{}; }; } // namespace Service::Glue diff --git a/src/core/hle/service/grc/grc.cpp b/src/core/hle/service/grc/grc.cpp index f918bdf03..4b684f6d0 100644 --- a/src/core/hle/service/grc/grc.cpp +++ b/src/core/hle/service/grc/grc.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/grc/grc.h b/src/core/hle/service/grc/grc.h index 9069fe756..f8c2f8dab 100644 --- a/src/core/hle/service/grc/grc.h +++ b/src/core/hle/service/grc/grc.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/console_sixaxis.cpp index a727b3582..bb3cba910 100644 --- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp +++ b/src/core/hle/service/hid/controllers/console_sixaxis.cpp @@ -1,8 +1,6 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later -#include "common/settings.h" #include "core/core_timing.h" #include "core/hid/emulated_console.h" #include "core/hid/hid_core.h" @@ -11,9 +9,14 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200; -Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_) +Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_, + u8* raw_shared_memory_) : ControllerBase{hid_core_} { console = hid_core.GetEmulatedConsole(); + static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size, + "ConsoleSharedMemory is bigger than the shared memory"); + shared_memory = std::construct_at( + reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); } Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default; @@ -22,8 +25,7 @@ void Controller_ConsoleSixAxis::OnInit() {} void Controller_ConsoleSixAxis::OnRelease() {} -void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) { +void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated() || !is_transfer_memory_set) { seven_sixaxis_lifo.buffer_count = 0; seven_sixaxis_lifo.buffer_tail = 0; @@ -50,13 +52,11 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti -motion_status.quaternion.xyz.z, }; - console_six_axis.sampling_number++; - console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest; - console_six_axis.verticalization_error = motion_status.verticalization_error; - console_six_axis.gyro_bias = motion_status.gyro_bias; + shared_memory->sampling_number++; + shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest; + shared_memory->verticalization_error = motion_status.verticalization_error; + shared_memory->gyro_bias = motion_status.gyro_bias; - // Update console six axis shared memory - std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis)); // Update seven six axis transfer memory seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state); std::memcpy(transfer_memory, &seven_sixaxis_lifo, sizeof(seven_sixaxis_lifo)); diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/console_sixaxis.h index 26d153f0c..2fd11538f 100644 --- a/src/core/hle/service/hid/controllers/console_sixaxis.h +++ b/src/core/hle/service/hid/controllers/console_sixaxis.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,7 +7,6 @@ #include "common/common_types.h" #include "common/quaternion.h" -#include "core/hid/hid_types.h" #include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/ring_lifo.h" @@ -19,7 +17,7 @@ class EmulatedConsole; namespace Service::HID { class Controller_ConsoleSixAxis final : public ControllerBase { public: - explicit Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_); + explicit Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); ~Controller_ConsoleSixAxis() override; // Called when the controller is initialized @@ -29,7 +27,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; // Called on InitializeSevenSixAxisSensor void SetTransferMemoryPointer(u8* t_mem); @@ -63,12 +61,13 @@ private: Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{}; static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size"); - Core::HID::EmulatedConsole* console; + SevenSixAxisState next_seven_sixaxis_state{}; u8* transfer_memory = nullptr; + ConsoleSharedMemory* shared_memory = nullptr; + Core::HID::EmulatedConsole* console = nullptr; + bool is_transfer_memory_set = false; u64 last_saved_timestamp{}; u64 last_global_timestamp{}; - ConsoleSharedMemory console_six_axis{}; - SevenSixAxisState next_seven_sixaxis_state{}; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp index 788ae9ae7..c58d67d7d 100644 --- a/src/core/hle/service/hid/controllers/controller_base.cpp +++ b/src/core/hle/service/hid/controllers/controller_base.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/hid/controllers/controller_base.h" diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h index 7450eb20a..d6f7a5073 100644 --- a/src/core/hle/service/hid/controllers/controller_base.h +++ b/src/core/hle/service/hid/controllers/controller_base.h @@ -1,11 +1,9 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include "common/common_types.h" -#include "common/swap.h" namespace Core::Timing { class CoreTiming; @@ -28,12 +26,10 @@ public: virtual void OnRelease() = 0; // When the controller is requesting an update for the shared memory - virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) = 0; + virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing) = 0; // When the controller is requesting a motion update for the shared memory - virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) {} + virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {} void ActivateController(); @@ -42,6 +38,7 @@ public: bool IsControllerActivated() const; static const std::size_t hid_entry_count = 17; + static const std::size_t shared_memory_size = 0x40000; protected: bool is_activated{false}; diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index 6a6fb9cab..8ec9f4a95 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include "common/common_types.h" @@ -14,8 +13,12 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000; -Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_) +Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} { + static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size, + "DebugPadSharedMemory is bigger than the shared memory"); + shared_memory = std::construct_at( + reinterpret_cast<DebugPadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); } @@ -25,16 +28,14 @@ void Controller_DebugPad::OnInit() {} void Controller_DebugPad::OnRelease() {} -void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) { +void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { - debug_pad_lifo.buffer_count = 0; - debug_pad_lifo.buffer_tail = 0; - std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo)); + shared_memory->debug_pad_lifo.buffer_count = 0; + shared_memory->debug_pad_lifo.buffer_tail = 0; return; } - const auto& last_entry = debug_pad_lifo.ReadCurrentEntry().state; + const auto& last_entry = shared_memory->debug_pad_lifo.ReadCurrentEntry().state; next_state.sampling_number = last_entry.sampling_number + 1; if (Settings::values.debug_pad_enabled) { @@ -48,8 +49,7 @@ void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, next_state.r_stick = stick_state.right; } - debug_pad_lifo.WriteNextEntry(next_state); - std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo)); + shared_memory->debug_pad_lifo.WriteNextEntry(next_state); } } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h index afe374fc2..68ff0ea79 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.h +++ b/src/core/hle/service/hid/controllers/debug_pad.h @@ -1,14 +1,10 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <array> #include "common/bit_field.h" -#include "common/common_funcs.h" #include "common/common_types.h" -#include "common/swap.h" #include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/ring_lifo.h" @@ -21,7 +17,7 @@ struct AnalogStickState; namespace Service::HID { class Controller_DebugPad final : public ControllerBase { public: - explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_); + explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); ~Controller_DebugPad() override; // Called when the controller is initialized @@ -31,7 +27,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; private: // This is nn::hid::DebugPadAttribute @@ -45,19 +41,24 @@ private: // This is nn::hid::DebugPadState struct DebugPadState { - s64 sampling_number; - DebugPadAttribute attribute; - Core::HID::DebugPadButton pad_state; - Core::HID::AnalogStickState r_stick; - Core::HID::AnalogStickState l_stick; + s64 sampling_number{}; + DebugPadAttribute attribute{}; + Core::HID::DebugPadButton pad_state{}; + Core::HID::AnalogStickState r_stick{}; + Core::HID::AnalogStickState l_stick{}; }; static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state"); - // This is nn::hid::detail::DebugPadLifo - Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{}; - static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size"); - DebugPadState next_state{}; + struct DebugPadSharedMemory { + // This is nn::hid::detail::DebugPadLifo + Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{}; + static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size"); + INSERT_PADDING_WORDS(0x4E); + }; + static_assert(sizeof(DebugPadSharedMemory) == 0x400, "DebugPadSharedMemory is an invalid size"); - Core::HID::EmulatedController* controller; + DebugPadState next_state{}; + DebugPadSharedMemory* shared_memory = nullptr; + Core::HID::EmulatedController* controller = nullptr; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index fe895c4f6..32e0708ba 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "common/math_util.h" @@ -24,25 +23,28 @@ constexpr f32 Square(s32 num) { return static_cast<f32>(num * num); } -Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) { +Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) + : ControllerBase(hid_core_) { + static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size, + "GestureSharedMemory is bigger than the shared memory"); + shared_memory = std::construct_at( + reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); console = hid_core.GetEmulatedConsole(); } Controller_Gesture::~Controller_Gesture() = default; void Controller_Gesture::OnInit() { - gesture_lifo.buffer_count = 0; - gesture_lifo.buffer_tail = 0; + shared_memory->gesture_lifo.buffer_count = 0; + shared_memory->gesture_lifo.buffer_tail = 0; force_update = true; } void Controller_Gesture::OnRelease() {} -void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) { +void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { - gesture_lifo.buffer_count = 0; - gesture_lifo.buffer_tail = 0; - std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo)); + shared_memory->gesture_lifo.buffer_count = 0; + shared_memory->gesture_lifo.buffer_tail = 0; return; } @@ -50,15 +52,16 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u GestureProperties gesture = GetGestureProperties(); f32 time_difference = - static_cast<f32>(gesture_lifo.timestamp - last_update_timestamp) / (1000 * 1000 * 1000); + static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) / + (1000 * 1000 * 1000); // Only update if necesary if (!ShouldUpdateGesture(gesture, time_difference)) { return; } - last_update_timestamp = gesture_lifo.timestamp; - UpdateGestureSharedMemory(data, size, gesture, time_difference); + last_update_timestamp = shared_memory->gesture_lifo.timestamp; + UpdateGestureSharedMemory(gesture, time_difference); } void Controller_Gesture::ReadTouchInput() { @@ -92,13 +95,12 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, return false; } -void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, - GestureProperties& gesture, +void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) { GestureType type = GestureType::Idle; GestureAttribute attributes{}; - const auto& last_entry = gesture_lifo.ReadCurrentEntry().state; + const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state; // Reset next state to default next_state.sampling_number = last_entry.sampling_number + 1; @@ -128,8 +130,7 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, next_state.points = gesture.points; last_gesture = gesture; - gesture_lifo.WriteNextEntry(next_state); - std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo)); + shared_memory->gesture_lifo.WriteNextEntry(next_state); } void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type, @@ -306,7 +307,7 @@ void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, } const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const { - return gesture_lifo.ReadCurrentEntry().state; + return shared_memory->gesture_lifo.ReadCurrentEntry().state; } Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() { diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h index 0936a3fa3..0d6099ea0 100644 --- a/src/core/hle/service/hid/controllers/gesture.h +++ b/src/core/hle/service/hid/controllers/gesture.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -15,7 +14,7 @@ namespace Service::HID { class Controller_Gesture final : public ControllerBase { public: - explicit Controller_Gesture(Core::HID::HIDCore& hid_core_); + explicit Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); ~Controller_Gesture() override; // Called when the controller is initialized @@ -25,7 +24,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; private: static constexpr size_t MAX_FINGERS = 16; @@ -67,19 +66,19 @@ private: // This is nn::hid::GestureState struct GestureState { - s64 sampling_number; - s64 detection_count; - GestureType type; - GestureDirection direction; - Common::Point<s32> pos; - Common::Point<s32> delta; - f32 vel_x; - f32 vel_y; - GestureAttribute attributes; - f32 scale; - f32 rotation_angle; - s32 point_count; - std::array<Common::Point<s32>, 4> points; + s64 sampling_number{}; + s64 detection_count{}; + GestureType type{GestureType::Idle}; + GestureDirection direction{GestureDirection::None}; + Common::Point<s32> pos{}; + Common::Point<s32> delta{}; + f32 vel_x{}; + f32 vel_y{}; + GestureAttribute attributes{}; + f32 scale{}; + f32 rotation_angle{}; + s32 point_count{}; + std::array<Common::Point<s32>, 4> points{}; }; static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); @@ -93,6 +92,14 @@ private: f32 angle{}; }; + struct GestureSharedMemory { + // This is nn::hid::detail::GestureLifo + Lifo<GestureState, hid_entry_count> gesture_lifo{}; + static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size"); + INSERT_PADDING_WORDS(0x3E); + }; + static_assert(sizeof(GestureSharedMemory) == 0x800, "GestureSharedMemory is an invalid size"); + // Reads input from all available input engines void ReadTouchInput(); @@ -100,8 +107,7 @@ private: bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference); // Updates the shared memory to the next state - void UpdateGestureSharedMemory(u8* data, std::size_t size, GestureProperties& gesture, - f32 time_difference); + void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference); // Initializes new gesture void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes); @@ -135,12 +141,9 @@ private: // Returns the average distance, angle and middle point of the active fingers GestureProperties GetGestureProperties(); - // This is nn::hid::detail::GestureLifo - Lifo<GestureState, hid_entry_count> gesture_lifo{}; - static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size"); GestureState next_state{}; - - Core::HID::EmulatedConsole* console; + GestureSharedMemory* shared_memory = nullptr; + Core::HID::EmulatedConsole* console = nullptr; std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{}; GestureProperties last_gesture{}; diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index 9588a6910..117d87433 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include "common/common_types.h" @@ -13,8 +12,12 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; -Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_) +Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} { + static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size, + "KeyboardSharedMemory is bigger than the shared memory"); + shared_memory = std::construct_at( + reinterpret_cast<KeyboardSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); emulated_devices = hid_core.GetEmulatedDevices(); } @@ -24,16 +27,14 @@ void Controller_Keyboard::OnInit() {} void Controller_Keyboard::OnRelease() {} -void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) { +void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { - keyboard_lifo.buffer_count = 0; - keyboard_lifo.buffer_tail = 0; - std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo)); + shared_memory->keyboard_lifo.buffer_count = 0; + shared_memory->keyboard_lifo.buffer_tail = 0; return; } - const auto& last_entry = keyboard_lifo.ReadCurrentEntry().state; + const auto& last_entry = shared_memory->keyboard_lifo.ReadCurrentEntry().state; next_state.sampling_number = last_entry.sampling_number + 1; if (Settings::values.keyboard_enabled) { @@ -45,8 +46,7 @@ void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, next_state.attribute.is_connected.Assign(1); } - keyboard_lifo.WriteNextEntry(next_state); - std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo)); + shared_memory->keyboard_lifo.WriteNextEntry(next_state); } } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h index cf62d3896..7532f53c6 100644 --- a/src/core/hle/service/hid/controllers/keyboard.h +++ b/src/core/hle/service/hid/controllers/keyboard.h @@ -1,14 +1,9 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <array> -#include "common/bit_field.h" -#include "common/common_funcs.h" #include "common/common_types.h" -#include "common/swap.h" #include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/ring_lifo.h" @@ -21,7 +16,7 @@ struct KeyboardKey; namespace Service::HID { class Controller_Keyboard final : public ControllerBase { public: - explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_); + explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); ~Controller_Keyboard() override; // Called when the controller is initialized @@ -31,23 +26,28 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; private: // This is nn::hid::detail::KeyboardState struct KeyboardState { - s64 sampling_number; - Core::HID::KeyboardModifier modifier; - Core::HID::KeyboardAttribute attribute; - Core::HID::KeyboardKey key; + s64 sampling_number{}; + Core::HID::KeyboardModifier modifier{}; + Core::HID::KeyboardAttribute attribute{}; + Core::HID::KeyboardKey key{}; }; static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size"); - // This is nn::hid::detail::KeyboardLifo - Lifo<KeyboardState, hid_entry_count> keyboard_lifo{}; - static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size"); - KeyboardState next_state{}; + struct KeyboardSharedMemory { + // This is nn::hid::detail::KeyboardLifo + Lifo<KeyboardState, hid_entry_count> keyboard_lifo{}; + static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size"); + INSERT_PADDING_WORDS(0xA); + }; + static_assert(sizeof(KeyboardSharedMemory) == 0x400, "KeyboardSharedMemory is an invalid size"); - Core::HID::EmulatedDevices* emulated_devices; + KeyboardState next_state{}; + KeyboardSharedMemory* shared_memory = nullptr; + Core::HID::EmulatedDevices* emulated_devices = nullptr; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index ba79888ae..b11cb438d 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include "common/common_types.h" @@ -13,7 +12,12 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; -Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} { +Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) + : ControllerBase{hid_core_} { + static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size, + "MouseSharedMemory is bigger than the shared memory"); + shared_memory = std::construct_at( + reinterpret_cast<MouseSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); emulated_devices = hid_core.GetEmulatedDevices(); } @@ -22,16 +26,14 @@ Controller_Mouse::~Controller_Mouse() = default; void Controller_Mouse::OnInit() {} void Controller_Mouse::OnRelease() {} -void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) { +void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { - mouse_lifo.buffer_count = 0; - mouse_lifo.buffer_tail = 0; - std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo)); + shared_memory->mouse_lifo.buffer_count = 0; + shared_memory->mouse_lifo.buffer_tail = 0; return; } - const auto& last_entry = mouse_lifo.ReadCurrentEntry().state; + const auto& last_entry = shared_memory->mouse_lifo.ReadCurrentEntry().state; next_state.sampling_number = last_entry.sampling_number + 1; next_state.attribute.raw = 0; @@ -51,8 +53,7 @@ void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* next_state.button = mouse_button_state; } - mouse_lifo.WriteNextEntry(next_state); - std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo)); + shared_memory->mouse_lifo.WriteNextEntry(next_state); } } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h index 7559fc78d..733d00577 100644 --- a/src/core/hle/service/hid/controllers/mouse.h +++ b/src/core/hle/service/hid/controllers/mouse.h @@ -1,13 +1,9 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <array> -#include "common/bit_field.h" #include "common/common_types.h" -#include "common/swap.h" #include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/ring_lifo.h" @@ -20,7 +16,7 @@ struct AnalogStickState; namespace Service::HID { class Controller_Mouse final : public ControllerBase { public: - explicit Controller_Mouse(Core::HID::HIDCore& hid_core_); + explicit Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); ~Controller_Mouse() override; // Called when the controller is initialized @@ -30,15 +26,20 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; private: - // This is nn::hid::detail::MouseLifo - Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{}; - static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size"); - Core::HID::MouseState next_state{}; + struct MouseSharedMemory { + // This is nn::hid::detail::MouseLifo + Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{}; + static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size"); + INSERT_PADDING_WORDS(0x2C); + }; + static_assert(sizeof(MouseSharedMemory) == 0x400, "MouseSharedMemory is an invalid size"); - Core::HID::AnalogStickState last_mouse_wheel_state; - Core::HID::EmulatedDevices* emulated_devices; + Core::HID::MouseState next_state{}; + Core::HID::AnalogStickState last_mouse_wheel_state{}; + MouseSharedMemory* shared_memory = nullptr; + Core::HID::EmulatedDevices* emulated_devices = nullptr; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index e5c951e06..f8972ec7a 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -1,10 +1,11 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> +#include <chrono> #include <cstring> + #include "common/assert.h" #include "common/bit_field.h" #include "common/common_types.h" @@ -17,6 +18,7 @@ #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/errors.h" #include "core/hle/service/kernel_helpers.h" namespace Service::HID { @@ -47,23 +49,52 @@ bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) { } } -bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { - return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) && - device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType && - device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; +Result Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { + const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); + const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; + const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; + + if (!npad_type) { + return VibrationInvalidStyleIndex; + } + if (!npad_id) { + return VibrationInvalidNpadId; + } + if (!device_index) { + return VibrationDeviceIndexOutOfRange; + } + + return ResultSuccess; } -bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) { - return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) && - device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType && - device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; +Result Controller_NPad::VerifyValidSixAxisSensorHandle( + const Core::HID::SixAxisSensorHandle& device_handle) { + const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); + const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; + const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; + + if (!npad_id) { + return InvalidNpadId; + } + if (!device_index) { + return NpadDeviceIndexOutOfRange; + } + // This doesn't get validated on nnsdk + if (!npad_type) { + return NpadInvalidHandle; + } + + return ResultSuccess; } -Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, +Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, KernelHelpers::ServiceContext& service_context_) : ControllerBase{hid_core_}, service_context{service_context_} { + static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size); for (std::size_t i = 0; i < controller_data.size(); ++i) { auto& controller = controller_data[i]; + controller.shared_memory = std::construct_at(reinterpret_cast<NpadInternalState*>( + raw_shared_memory_ + NPAD_OFFSET + (i * sizeof(NpadInternalState)))); controller.device = hid_core.GetEmulatedControllerByIndex(i); controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value = Core::HID::DEFAULT_VIBRATION_VALUE; @@ -113,11 +144,11 @@ void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, if (!controller.device->IsConnected()) { return; } - auto& shared_memory = controller.shared_memory_entry; + auto* shared_memory = controller.shared_memory; const auto& battery_level = controller.device->GetBattery(); - shared_memory.battery_level_dual = battery_level.dual.battery_level; - shared_memory.battery_level_left = battery_level.left.battery_level; - shared_memory.battery_level_right = battery_level.right.battery_level; + shared_memory->battery_level_dual = battery_level.dual.battery_level; + shared_memory->battery_level_left = battery_level.left.battery_level; + shared_memory->battery_level_right = battery_level.right.battery_level; break; } default: @@ -132,123 +163,178 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { } LOG_DEBUG(Service_HID, "Npad connected {}", npad_id); const auto controller_type = controller.device->GetNpadStyleIndex(); - auto& shared_memory = controller.shared_memory_entry; + const auto& body_colors = controller.device->GetColors(); + const auto& battery_level = controller.device->GetBattery(); + auto* shared_memory = controller.shared_memory; if (controller_type == Core::HID::NpadStyleIndex::None) { controller.styleset_changed_event->GetWritableEvent().Signal(); return; } - shared_memory.style_tag.raw = Core::HID::NpadStyleSet::None; - shared_memory.device_type.raw = 0; - shared_memory.system_properties.raw = 0; + + // Reset memory values + shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None; + shared_memory->device_type.raw = 0; + shared_memory->system_properties.raw = 0; + shared_memory->joycon_color.attribute = ColorAttribute::NoController; + shared_memory->joycon_color.attribute = ColorAttribute::NoController; + shared_memory->fullkey_color = {}; + shared_memory->joycon_color.left = {}; + shared_memory->joycon_color.right = {}; + shared_memory->battery_level_dual = {}; + shared_memory->battery_level_left = {}; + shared_memory->battery_level_right = {}; + switch (controller_type) { case Core::HID::NpadStyleIndex::None: - UNREACHABLE(); + ASSERT(false); break; case Core::HID::NpadStyleIndex::ProController: - shared_memory.style_tag.fullkey.Assign(1); - shared_memory.device_type.fullkey.Assign(1); - shared_memory.system_properties.is_vertical.Assign(1); - shared_memory.system_properties.use_plus.Assign(1); - shared_memory.system_properties.use_minus.Assign(1); - shared_memory.applet_footer.type = AppletFooterUiType::SwitchProController; + shared_memory->fullkey_color.attribute = ColorAttribute::Ok; + shared_memory->fullkey_color.fullkey = body_colors.fullkey; + shared_memory->battery_level_dual = battery_level.dual.battery_level; + shared_memory->style_tag.fullkey.Assign(1); + shared_memory->device_type.fullkey.Assign(1); + shared_memory->system_properties.is_vertical.Assign(1); + shared_memory->system_properties.use_plus.Assign(1); + shared_memory->system_properties.use_minus.Assign(1); + shared_memory->system_properties.is_charging_joy_dual.Assign( + battery_level.dual.is_charging); + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController; + shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1); break; case Core::HID::NpadStyleIndex::Handheld: - shared_memory.style_tag.handheld.Assign(1); - shared_memory.device_type.handheld_left.Assign(1); - shared_memory.device_type.handheld_right.Assign(1); - shared_memory.system_properties.is_vertical.Assign(1); - shared_memory.system_properties.use_plus.Assign(1); - shared_memory.system_properties.use_minus.Assign(1); - shared_memory.system_properties.use_directional_buttons.Assign(1); - shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual; - shared_memory.applet_footer.type = AppletFooterUiType::HandheldJoyConLeftJoyConRight; + shared_memory->fullkey_color.attribute = ColorAttribute::Ok; + shared_memory->joycon_color.attribute = ColorAttribute::Ok; + shared_memory->fullkey_color.fullkey = body_colors.fullkey; + shared_memory->joycon_color.left = body_colors.left; + shared_memory->joycon_color.right = body_colors.right; + shared_memory->style_tag.handheld.Assign(1); + shared_memory->device_type.handheld_left.Assign(1); + shared_memory->device_type.handheld_right.Assign(1); + shared_memory->system_properties.is_vertical.Assign(1); + shared_memory->system_properties.use_plus.Assign(1); + shared_memory->system_properties.use_minus.Assign(1); + shared_memory->system_properties.use_directional_buttons.Assign(1); + shared_memory->system_properties.is_charging_joy_dual.Assign( + battery_level.left.is_charging); + shared_memory->system_properties.is_charging_joy_left.Assign( + battery_level.left.is_charging); + shared_memory->system_properties.is_charging_joy_right.Assign( + battery_level.right.is_charging); + shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; + shared_memory->applet_nfc_xcd.applet_footer.type = + AppletFooterUiType::HandheldJoyConLeftJoyConRight; + shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1); break; case Core::HID::NpadStyleIndex::JoyconDual: - shared_memory.style_tag.joycon_dual.Assign(1); + shared_memory->fullkey_color.attribute = ColorAttribute::Ok; + shared_memory->joycon_color.attribute = ColorAttribute::Ok; + shared_memory->style_tag.joycon_dual.Assign(1); if (controller.is_dual_left_connected) { - shared_memory.device_type.joycon_left.Assign(1); - shared_memory.system_properties.use_minus.Assign(1); + shared_memory->joycon_color.left = body_colors.left; + shared_memory->battery_level_left = battery_level.left.battery_level; + shared_memory->device_type.joycon_left.Assign(1); + shared_memory->system_properties.use_minus.Assign(1); + shared_memory->system_properties.is_charging_joy_left.Assign( + battery_level.left.is_charging); + shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1); } if (controller.is_dual_right_connected) { - shared_memory.device_type.joycon_right.Assign(1); - shared_memory.system_properties.use_plus.Assign(1); + shared_memory->joycon_color.right = body_colors.right; + shared_memory->battery_level_right = battery_level.right.battery_level; + shared_memory->device_type.joycon_right.Assign(1); + shared_memory->system_properties.use_plus.Assign(1); + shared_memory->system_properties.is_charging_joy_right.Assign( + battery_level.right.is_charging); + shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1); } - shared_memory.system_properties.use_directional_buttons.Assign(1); - shared_memory.system_properties.is_vertical.Assign(1); - shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual; + shared_memory->system_properties.use_directional_buttons.Assign(1); + shared_memory->system_properties.is_vertical.Assign(1); + shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; + if (controller.is_dual_left_connected && controller.is_dual_right_connected) { - shared_memory.applet_footer.type = AppletFooterUiType::JoyDual; + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDual; + shared_memory->fullkey_color.fullkey = body_colors.left; + shared_memory->battery_level_dual = battery_level.left.battery_level; + shared_memory->system_properties.is_charging_joy_dual.Assign( + battery_level.left.is_charging); } else if (controller.is_dual_left_connected) { - shared_memory.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly; + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly; + shared_memory->fullkey_color.fullkey = body_colors.left; + shared_memory->battery_level_dual = battery_level.left.battery_level; + shared_memory->system_properties.is_charging_joy_dual.Assign( + battery_level.left.is_charging); } else { - shared_memory.applet_footer.type = AppletFooterUiType::JoyDualRightOnly; + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualRightOnly; + shared_memory->fullkey_color.fullkey = body_colors.right; + shared_memory->battery_level_dual = battery_level.right.battery_level; + shared_memory->system_properties.is_charging_joy_dual.Assign( + battery_level.right.is_charging); } break; case Core::HID::NpadStyleIndex::JoyconLeft: - shared_memory.style_tag.joycon_left.Assign(1); - shared_memory.device_type.joycon_left.Assign(1); - shared_memory.system_properties.is_horizontal.Assign(1); - shared_memory.system_properties.use_minus.Assign(1); - shared_memory.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal; + shared_memory->joycon_color.attribute = ColorAttribute::Ok; + shared_memory->joycon_color.left = body_colors.left; + shared_memory->battery_level_dual = battery_level.left.battery_level; + shared_memory->style_tag.joycon_left.Assign(1); + shared_memory->device_type.joycon_left.Assign(1); + shared_memory->system_properties.is_horizontal.Assign(1); + shared_memory->system_properties.use_minus.Assign(1); + shared_memory->system_properties.is_charging_joy_left.Assign( + battery_level.left.is_charging); + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal; + shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1); break; case Core::HID::NpadStyleIndex::JoyconRight: - shared_memory.style_tag.joycon_right.Assign(1); - shared_memory.device_type.joycon_right.Assign(1); - shared_memory.system_properties.is_horizontal.Assign(1); - shared_memory.system_properties.use_plus.Assign(1); - shared_memory.applet_footer.type = AppletFooterUiType::JoyRightHorizontal; + shared_memory->joycon_color.attribute = ColorAttribute::Ok; + shared_memory->joycon_color.right = body_colors.right; + shared_memory->battery_level_right = battery_level.right.battery_level; + shared_memory->style_tag.joycon_right.Assign(1); + shared_memory->device_type.joycon_right.Assign(1); + shared_memory->system_properties.is_horizontal.Assign(1); + shared_memory->system_properties.use_plus.Assign(1); + shared_memory->system_properties.is_charging_joy_right.Assign( + battery_level.right.is_charging); + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal; + shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1); break; case Core::HID::NpadStyleIndex::GameCube: - shared_memory.style_tag.gamecube.Assign(1); - shared_memory.device_type.fullkey.Assign(1); - shared_memory.system_properties.is_vertical.Assign(1); - shared_memory.system_properties.use_plus.Assign(1); + shared_memory->style_tag.gamecube.Assign(1); + shared_memory->device_type.fullkey.Assign(1); + shared_memory->system_properties.is_vertical.Assign(1); + shared_memory->system_properties.use_plus.Assign(1); break; case Core::HID::NpadStyleIndex::Pokeball: - shared_memory.style_tag.palma.Assign(1); - shared_memory.device_type.palma.Assign(1); + shared_memory->style_tag.palma.Assign(1); + shared_memory->device_type.palma.Assign(1); + shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1); break; case Core::HID::NpadStyleIndex::NES: - shared_memory.style_tag.lark.Assign(1); - shared_memory.device_type.fullkey.Assign(1); + shared_memory->style_tag.lark.Assign(1); + shared_memory->device_type.fullkey.Assign(1); break; case Core::HID::NpadStyleIndex::SNES: - shared_memory.style_tag.lucia.Assign(1); - shared_memory.device_type.fullkey.Assign(1); - shared_memory.applet_footer.type = AppletFooterUiType::Lucia; + shared_memory->style_tag.lucia.Assign(1); + shared_memory->device_type.fullkey.Assign(1); + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::Lucia; break; case Core::HID::NpadStyleIndex::N64: - shared_memory.style_tag.lagoon.Assign(1); - shared_memory.device_type.fullkey.Assign(1); - shared_memory.applet_footer.type = AppletFooterUiType::Lagon; + shared_memory->style_tag.lagoon.Assign(1); + shared_memory->device_type.fullkey.Assign(1); + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::Lagon; break; case Core::HID::NpadStyleIndex::SegaGenesis: - shared_memory.style_tag.lager.Assign(1); - shared_memory.device_type.fullkey.Assign(1); + shared_memory->style_tag.lager.Assign(1); + shared_memory->device_type.fullkey.Assign(1); break; default: break; } - const auto& body_colors = controller.device->GetColors(); - - shared_memory.fullkey_color.attribute = ColorAttribute::Ok; - shared_memory.fullkey_color.fullkey = body_colors.fullkey; - - shared_memory.joycon_color.attribute = ColorAttribute::Ok; - shared_memory.joycon_color.left = body_colors.left; - shared_memory.joycon_color.right = body_colors.right; - - // TODO: Investigate when we should report all batery types - const auto& battery_level = controller.device->GetBattery(); - shared_memory.battery_level_dual = battery_level.dual.battery_level; - shared_memory.battery_level_left = battery_level.left.battery_level; - shared_memory.battery_level_right = battery_level.right.battery_level; - controller.is_connected = true; controller.device->Connect(); SignalStyleSetChangedEvent(npad_id); - WriteEmptyEntry(controller.shared_memory_entry); + WriteEmptyEntry(controller.shared_memory); } void Controller_NPad::OnInit() { @@ -262,23 +348,18 @@ void Controller_NPad::OnInit() { service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i)); } - if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) { - // We want to support all controllers - hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All}); - } - supported_npad_id_types.resize(npad_id_list.size()); std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), npad_id_list.size() * sizeof(Core::HID::NpadIdType)); // Prefill controller buffers for (auto& controller : controller_data) { - auto& npad = controller.shared_memory_entry; - npad.fullkey_color = { + auto* npad = controller.shared_memory; + npad->fullkey_color = { .attribute = ColorAttribute::NoController, .fullkey = {}, }; - npad.joycon_color = { + npad->joycon_color = { .attribute = ColorAttribute::NoController, .left = {}, .right = {}, @@ -288,38 +369,31 @@ void Controller_NPad::OnInit() { WriteEmptyEntry(npad); } } - - // Connect controllers - for (auto& controller : controller_data) { - const auto& device = controller.device; - if (device->IsConnected()) { - AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType()); - } - } } -void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) { +void Controller_NPad::WriteEmptyEntry(NpadInternalState* npad) { NPadGenericState dummy_pad_state{}; NpadGcTriggerState dummy_gc_state{}; - dummy_pad_state.sampling_number = npad.fullkey_lifo.ReadCurrentEntry().sampling_number + 1; - npad.fullkey_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad.handheld_lifo.ReadCurrentEntry().sampling_number + 1; - npad.handheld_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad.joy_dual_lifo.ReadCurrentEntry().sampling_number + 1; - npad.joy_dual_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad.joy_left_lifo.ReadCurrentEntry().sampling_number + 1; - npad.joy_left_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad.joy_right_lifo.ReadCurrentEntry().sampling_number + 1; - npad.joy_right_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad.palma_lifo.ReadCurrentEntry().sampling_number + 1; - npad.palma_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad.system_ext_lifo.ReadCurrentEntry().sampling_number + 1; - npad.system_ext_lifo.WriteNextEntry(dummy_pad_state); - dummy_gc_state.sampling_number = npad.gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1; - npad.gc_trigger_lifo.WriteNextEntry(dummy_gc_state); + dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1; + npad->fullkey_lifo.WriteNextEntry(dummy_pad_state); + dummy_pad_state.sampling_number = npad->handheld_lifo.ReadCurrentEntry().sampling_number + 1; + npad->handheld_lifo.WriteNextEntry(dummy_pad_state); + dummy_pad_state.sampling_number = npad->joy_dual_lifo.ReadCurrentEntry().sampling_number + 1; + npad->joy_dual_lifo.WriteNextEntry(dummy_pad_state); + dummy_pad_state.sampling_number = npad->joy_left_lifo.ReadCurrentEntry().sampling_number + 1; + npad->joy_left_lifo.WriteNextEntry(dummy_pad_state); + dummy_pad_state.sampling_number = npad->joy_right_lifo.ReadCurrentEntry().sampling_number + 1; + npad->joy_right_lifo.WriteNextEntry(dummy_pad_state); + dummy_pad_state.sampling_number = npad->palma_lifo.ReadCurrentEntry().sampling_number + 1; + npad->palma_lifo.WriteNextEntry(dummy_pad_state); + dummy_pad_state.sampling_number = npad->system_ext_lifo.ReadCurrentEntry().sampling_number + 1; + npad->system_ext_lifo.WriteNextEntry(dummy_pad_state); + dummy_gc_state.sampling_number = npad->gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1; + npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state); } void Controller_NPad::OnRelease() { + is_controller_initialized = false; for (std::size_t i = 0; i < controller_data.size(); ++i) { auto& controller = controller_data[i]; service_context.CloseEvent(controller.styleset_changed_event); @@ -330,7 +404,7 @@ void Controller_NPad::OnRelease() { } void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; auto& controller = GetControllerFromNpadIdType(npad_id); const auto controller_type = controller.device->GetNpadStyleIndex(); if (!controller.device->IsConnected()) { @@ -381,23 +455,19 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { } } -void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t data_len) { +void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { return; } for (std::size_t i = 0; i < controller_data.size(); ++i) { auto& controller = controller_data[i]; - auto& npad = controller.shared_memory_entry; + auto* npad = controller.shared_memory; const auto& controller_type = controller.device->GetNpadStyleIndex(); if (controller_type == Core::HID::NpadStyleIndex::None || !controller.device->IsConnected()) { - // Refresh shared memory - std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)), - &controller.shared_memory_entry, sizeof(NpadInternalState)); continue; } @@ -412,7 +482,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* libnx_state.connection_status.is_connected.Assign(1); switch (controller_type) { case Core::HID::NpadStyleIndex::None: - UNREACHABLE(); + ASSERT(false); break; case Core::HID::NpadStyleIndex::ProController: case Core::HID::NpadStyleIndex::NES: @@ -425,8 +495,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* libnx_state.connection_status.is_wired.Assign(1); pad_state.sampling_number = - npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad.fullkey_lifo.WriteNextEntry(pad_state); + npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->fullkey_lifo.WriteNextEntry(pad_state); break; case Core::HID::NpadStyleIndex::Handheld: pad_state.connection_status.raw = 0; @@ -443,8 +513,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* libnx_state.connection_status.is_left_wired.Assign(1); libnx_state.connection_status.is_right_wired.Assign(1); pad_state.sampling_number = - npad.handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad.handheld_lifo.WriteNextEntry(pad_state); + npad->handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->handheld_lifo.WriteNextEntry(pad_state); break; case Core::HID::NpadStyleIndex::JoyconDual: pad_state.connection_status.raw = 0; @@ -459,8 +529,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* } pad_state.sampling_number = - npad.joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad.joy_dual_lifo.WriteNextEntry(pad_state); + npad->joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->joy_dual_lifo.WriteNextEntry(pad_state); break; case Core::HID::NpadStyleIndex::JoyconLeft: pad_state.connection_status.raw = 0; @@ -469,8 +539,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* libnx_state.connection_status.is_left_connected.Assign(1); pad_state.sampling_number = - npad.joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad.joy_left_lifo.WriteNextEntry(pad_state); + npad->joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->joy_left_lifo.WriteNextEntry(pad_state); break; case Core::HID::NpadStyleIndex::JoyconRight: pad_state.connection_status.raw = 0; @@ -479,8 +549,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* libnx_state.connection_status.is_right_connected.Assign(1); pad_state.sampling_number = - npad.joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad.joy_right_lifo.WriteNextEntry(pad_state); + npad->joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->joy_right_lifo.WriteNextEntry(pad_state); break; case Core::HID::NpadStyleIndex::GameCube: pad_state.connection_status.raw = 0; @@ -489,18 +559,18 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* libnx_state.connection_status.is_wired.Assign(1); pad_state.sampling_number = - npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; trigger_state.sampling_number = - npad.gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad.fullkey_lifo.WriteNextEntry(pad_state); - npad.gc_trigger_lifo.WriteNextEntry(trigger_state); + npad->gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->fullkey_lifo.WriteNextEntry(pad_state); + npad->gc_trigger_lifo.WriteNextEntry(trigger_state); break; case Core::HID::NpadStyleIndex::Pokeball: pad_state.connection_status.raw = 0; pad_state.connection_status.is_connected.Assign(1); pad_state.sampling_number = - npad.palma_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad.palma_lifo.WriteNextEntry(pad_state); + npad->palma_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->palma_lifo.WriteNextEntry(pad_state); break; default: break; @@ -509,17 +579,13 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw; libnx_state.l_stick = pad_state.l_stick; libnx_state.r_stick = pad_state.r_stick; - npad.system_ext_lifo.WriteNextEntry(pad_state); + npad->system_ext_lifo.WriteNextEntry(pad_state); press_state |= static_cast<u64>(pad_state.npad_buttons.raw); - - std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)), - &controller.shared_memory_entry, sizeof(NpadInternalState)); } } -void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t data_len) { +void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { return; } @@ -534,7 +600,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing continue; } - auto& npad = controller.shared_memory_entry; + auto* npad = controller.shared_memory; const auto& motion_state = controller.device->GetMotions(); auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; auto& sixaxis_handheld_state = controller.sixaxis_handheld_state; @@ -543,6 +609,14 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; + // Clear previous state + sixaxis_fullkey_state = {}; + sixaxis_handheld_state = {}; + sixaxis_dual_left_state = {}; + sixaxis_dual_right_state = {}; + sixaxis_left_lifo_state = {}; + sixaxis_right_lifo_state = {}; + if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) { controller.sixaxis_at_rest = true; for (std::size_t e = 0; e < motion_state.size(); ++e) { @@ -551,109 +625,116 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing } } + const auto set_motion_state = [&](SixAxisSensorState& state, + const Core::HID::ControllerMotion& hid_state) { + using namespace std::literals::chrono_literals; + static constexpr SixAxisSensorState default_motion_state = { + .delta_time = std::chrono::nanoseconds(5ms).count(), + .accel = {0, 0, -1.0f}, + .orientation = + { + Common::Vec3f{1.0f, 0, 0}, + Common::Vec3f{0, 1.0f, 0}, + Common::Vec3f{0, 0, 1.0f}, + }, + .attribute = {1}, + }; + if (!controller.sixaxis_sensor_enabled) { + state = default_motion_state; + return; + } + if (!Settings::values.motion_enabled.GetValue()) { + state = default_motion_state; + return; + } + state.attribute.is_connected.Assign(1); + state.delta_time = std::chrono::nanoseconds(5ms).count(); + state.accel = hid_state.accel; + state.gyro = hid_state.gyro; + state.rotation = hid_state.rotation; + state.orientation = hid_state.orientation; + }; + switch (controller_type) { case Core::HID::NpadStyleIndex::None: - UNREACHABLE(); + ASSERT(false); break; case Core::HID::NpadStyleIndex::ProController: - sixaxis_fullkey_state.attribute.raw = 0; - if (controller.sixaxis_sensor_enabled) { - sixaxis_fullkey_state.attribute.is_connected.Assign(1); - sixaxis_fullkey_state.accel = motion_state[0].accel; - sixaxis_fullkey_state.gyro = motion_state[0].gyro; - sixaxis_fullkey_state.rotation = motion_state[0].rotation; - sixaxis_fullkey_state.orientation = motion_state[0].orientation; - } + set_motion_state(sixaxis_fullkey_state, motion_state[0]); break; case Core::HID::NpadStyleIndex::Handheld: - sixaxis_handheld_state.attribute.raw = 0; - if (controller.sixaxis_sensor_enabled) { - sixaxis_handheld_state.attribute.is_connected.Assign(1); - sixaxis_handheld_state.accel = motion_state[0].accel; - sixaxis_handheld_state.gyro = motion_state[0].gyro; - sixaxis_handheld_state.rotation = motion_state[0].rotation; - sixaxis_handheld_state.orientation = motion_state[0].orientation; - } + set_motion_state(sixaxis_handheld_state, motion_state[0]); break; case Core::HID::NpadStyleIndex::JoyconDual: - sixaxis_dual_left_state.attribute.raw = 0; - sixaxis_dual_right_state.attribute.raw = 0; - if (controller.sixaxis_sensor_enabled) { - // Set motion for the left joycon - sixaxis_dual_left_state.attribute.is_connected.Assign(1); - sixaxis_dual_left_state.accel = motion_state[0].accel; - sixaxis_dual_left_state.gyro = motion_state[0].gyro; - sixaxis_dual_left_state.rotation = motion_state[0].rotation; - sixaxis_dual_left_state.orientation = motion_state[0].orientation; - } - if (controller.sixaxis_sensor_enabled) { - // Set motion for the right joycon - sixaxis_dual_right_state.attribute.is_connected.Assign(1); - sixaxis_dual_right_state.accel = motion_state[1].accel; - sixaxis_dual_right_state.gyro = motion_state[1].gyro; - sixaxis_dual_right_state.rotation = motion_state[1].rotation; - sixaxis_dual_right_state.orientation = motion_state[1].orientation; - } + set_motion_state(sixaxis_dual_left_state, motion_state[0]); + set_motion_state(sixaxis_dual_right_state, motion_state[1]); break; case Core::HID::NpadStyleIndex::JoyconLeft: - sixaxis_left_lifo_state.attribute.raw = 0; - if (controller.sixaxis_sensor_enabled) { - sixaxis_left_lifo_state.attribute.is_connected.Assign(1); - sixaxis_left_lifo_state.accel = motion_state[0].accel; - sixaxis_left_lifo_state.gyro = motion_state[0].gyro; - sixaxis_left_lifo_state.rotation = motion_state[0].rotation; - sixaxis_left_lifo_state.orientation = motion_state[0].orientation; - } + set_motion_state(sixaxis_left_lifo_state, motion_state[0]); break; case Core::HID::NpadStyleIndex::JoyconRight: - sixaxis_right_lifo_state.attribute.raw = 0; - if (controller.sixaxis_sensor_enabled) { - sixaxis_right_lifo_state.attribute.is_connected.Assign(1); - sixaxis_right_lifo_state.accel = motion_state[1].accel; - sixaxis_right_lifo_state.gyro = motion_state[1].gyro; - sixaxis_right_lifo_state.rotation = motion_state[1].rotation; - sixaxis_right_lifo_state.orientation = motion_state[1].orientation; - } + set_motion_state(sixaxis_right_lifo_state, motion_state[1]); + break; + case Core::HID::NpadStyleIndex::Pokeball: + using namespace std::literals::chrono_literals; + set_motion_state(sixaxis_fullkey_state, motion_state[0]); + sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); break; default: break; } sixaxis_fullkey_state.sampling_number = - npad.sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; sixaxis_handheld_state.sampling_number = - npad.sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; sixaxis_dual_left_state.sampling_number = - npad.sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1; sixaxis_dual_right_state.sampling_number = - npad.sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1; sixaxis_left_lifo_state.sampling_number = - npad.sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1; sixaxis_right_lifo_state.sampling_number = - npad.sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1; if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) { // This buffer only is updated on handheld on HW - npad.sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state); + npad->sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state); } else { // Handheld doesn't update this buffer on HW - npad.sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state); + npad->sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state); } - npad.sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state); - npad.sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state); - npad.sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state); - npad.sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state); - std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)), - &controller.shared_memory_entry, sizeof(NpadInternalState)); + npad->sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state); + npad->sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state); + npad->sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state); + npad->sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state); } } void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { hid_core.SetSupportedStyleTag(style_set); + + if (is_controller_initialized) { + return; + } + + // Once SetSupportedStyleSet is called controllers are fully initialized + is_controller_initialized = true; + + // Connect all active controllers + for (auto& controller : controller_data) { + const auto& device = controller.device; + if (device->IsConnected()) { + AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType()); + } + } } Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { + if (!is_controller_initialized) { + return {Core::HID::NpadStyleSet::None}; + } return hid_core.GetSupportedStyleTag(); } @@ -674,6 +755,12 @@ std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const { } void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { + if (joy_hold_type != NpadJoyHoldType::Horizontal && + joy_hold_type != NpadJoyHoldType::Vertical) { + LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}", + joy_hold_type); + return; + } hold_type = joy_hold_type; } @@ -682,6 +769,11 @@ Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const { } void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { + if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { + ASSERT_MSG(false, "Activation mode should be always None, Single or Dual"); + return; + } + handheld_activation_mode = activation_mode; } @@ -697,20 +789,21 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode return communication_mode; } -void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type, - NpadJoyAssignmentMode assignment_mode) { +Result Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, + NpadJoyDeviceType npad_device_type, + NpadJoyAssignmentMode assignment_mode) { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - return; + return InvalidNpadId; } auto& controller = GetControllerFromNpadIdType(npad_id); - if (controller.shared_memory_entry.assignment_mode != assignment_mode) { - controller.shared_memory_entry.assignment_mode = assignment_mode; + if (controller.shared_memory->assignment_mode != assignment_mode) { + controller.shared_memory->assignment_mode = assignment_mode; } if (!controller.device->IsConnected()) { - return; + return ResultSuccess; } if (assignment_mode == NpadJoyAssignmentMode::Dual) { @@ -719,34 +812,34 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy controller.is_dual_left_connected = true; controller.is_dual_right_connected = false; UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); - return; + return ResultSuccess; } if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) { DisconnectNpad(npad_id); controller.is_dual_left_connected = false; controller.is_dual_right_connected = true; UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); - return; + return ResultSuccess; } - return; + return ResultSuccess; } // This is for NpadJoyAssignmentMode::Single // Only JoyconDual get affected by this function if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) { - return; + return ResultSuccess; } if (controller.is_dual_left_connected && !controller.is_dual_right_connected) { DisconnectNpad(npad_id); UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true); - return; + return ResultSuccess; } if (!controller.is_dual_left_connected && controller.is_dual_right_connected) { DisconnectNpad(npad_id); UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true); - return; + return ResultSuccess; } // We have two controllers connected to the same npad_id we need to split them @@ -764,6 +857,7 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy controller_2.is_dual_right_connected = false; UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true); } + return ResultSuccess; } bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, @@ -795,11 +889,11 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, const auto now = steady_clock::now(); - // Filter out non-zero vibrations that are within 10ms of each other. + // Filter out non-zero vibrations that are within 15ms of each other. if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) && duration_cast<milliseconds>( now - controller.vibration[device_index].last_vibration_timepoint) < - milliseconds(10)) { + milliseconds(15)) { return false; } @@ -815,7 +909,7 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, void Controller_NPad::VibrateController( const Core::HID::VibrationDeviceHandle& vibration_device_handle, const Core::HID::VibrationValue& vibration_value) { - if (!IsDeviceHandleValid(vibration_device_handle)) { + if (IsDeviceHandleValid(vibration_device_handle).IsError()) { return; } @@ -831,7 +925,7 @@ void Controller_NPad::VibrateController( } if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) { - UNREACHABLE_MSG("DeviceIndex should never be None!"); + ASSERT_MSG(false, "DeviceIndex should never be None!"); return; } @@ -878,7 +972,7 @@ void Controller_NPad::VibrateControllers( Core::HID::VibrationValue Controller_NPad::GetLastVibration( const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { - if (!IsDeviceHandleValid(vibration_device_handle)) { + if (IsDeviceHandleValid(vibration_device_handle).IsError()) { return {}; } @@ -889,7 +983,7 @@ Core::HID::VibrationValue Controller_NPad::GetLastVibration( void Controller_NPad::InitializeVibrationDevice( const Core::HID::VibrationDeviceHandle& vibration_device_handle) { - if (!IsDeviceHandleValid(vibration_device_handle)) { + if (IsDeviceHandleValid(vibration_device_handle).IsError()) { return; } @@ -916,7 +1010,7 @@ void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { bool Controller_NPad::IsVibrationDeviceMounted( const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { - if (!IsDeviceHandleValid(vibration_device_handle)) { + if (IsDeviceHandleValid(vibration_device_handle).IsError()) { return false; } @@ -959,10 +1053,10 @@ void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, InitNewlyAddedController(npad_id); } -void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { +Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - return; + return InvalidNpadId; } LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id); @@ -973,188 +1067,300 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { controller.vibration[device_idx].device_mounted = false; } - auto& shared_memory_entry = controller.shared_memory_entry; - // Don't reset shared_memory_entry.assignment_mode this value is persistent - shared_memory_entry.style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out - shared_memory_entry.device_type.raw = 0; - shared_memory_entry.system_properties.raw = 0; - shared_memory_entry.button_properties.raw = 0; - shared_memory_entry.battery_level_dual = 0; - shared_memory_entry.battery_level_left = 0; - shared_memory_entry.battery_level_right = 0; - shared_memory_entry.fullkey_color = { + auto* shared_memory = controller.shared_memory; + // Don't reset shared_memory->assignment_mode this value is persistent + shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out + shared_memory->device_type.raw = 0; + shared_memory->system_properties.raw = 0; + shared_memory->button_properties.raw = 0; + shared_memory->sixaxis_fullkey_properties.raw = 0; + shared_memory->sixaxis_handheld_properties.raw = 0; + shared_memory->sixaxis_dual_left_properties.raw = 0; + shared_memory->sixaxis_dual_right_properties.raw = 0; + shared_memory->sixaxis_left_properties.raw = 0; + shared_memory->sixaxis_right_properties.raw = 0; + shared_memory->battery_level_dual = 0; + shared_memory->battery_level_left = 0; + shared_memory->battery_level_right = 0; + shared_memory->fullkey_color = { .attribute = ColorAttribute::NoController, .fullkey = {}, }; - shared_memory_entry.joycon_color = { + shared_memory->joycon_color = { .attribute = ColorAttribute::NoController, .left = {}, .right = {}, }; - shared_memory_entry.applet_footer.type = AppletFooterUiType::None; + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::None; controller.is_dual_left_connected = true; controller.is_dual_right_connected = true; controller.is_connected = false; controller.device->Disconnect(); SignalStyleSetChangedEvent(npad_id); - WriteEmptyEntry(controller.shared_memory_entry); + WriteEmptyEntry(shared_memory); + return ResultSuccess; } +Result Controller_NPad::SetGyroscopeZeroDriftMode( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, GyroscopeZeroDriftMode drift_mode) { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } -void Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, - GyroscopeZeroDriftMode drift_mode) { - if (!IsDeviceHandleValid(sixaxis_handle)) { - LOG_ERROR(Service_HID, "Invalid handle"); - return; + auto& sixaxis = GetSixaxisState(sixaxis_handle); + sixaxis.gyroscope_zero_drift_mode = drift_mode; + + return ResultSuccess; +} + +Result Controller_NPad::GetGyroscopeZeroDriftMode( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + GyroscopeZeroDriftMode& drift_mode) const { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; } - auto& controller = GetControllerFromHandle(sixaxis_handle); - controller.gyroscope_zero_drift_mode = drift_mode; + + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + drift_mode = sixaxis.gyroscope_zero_drift_mode; + + return ResultSuccess; } -Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode( - Core::HID::SixAxisSensorHandle sixaxis_handle) const { - if (!IsDeviceHandleValid(sixaxis_handle)) { - LOG_ERROR(Service_HID, "Invalid handle"); - // Return the default value - return GyroscopeZeroDriftMode::Standard; +Result Controller_NPad::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool& is_at_rest) const { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; } + const auto& controller = GetControllerFromHandle(sixaxis_handle); - return controller.gyroscope_zero_drift_mode; + is_at_rest = controller.sixaxis_at_rest; + return ResultSuccess; } -bool Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const { - if (!IsDeviceHandleValid(sixaxis_handle)) { - LOG_ERROR(Service_HID, "Invalid handle"); - // Return the default value - return true; +Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; } - const auto& controller = GetControllerFromHandle(sixaxis_handle); - return controller.sixaxis_at_rest; + + const auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle); + is_firmware_available = sixaxis_properties.is_firmware_update_available != 0; + return ResultSuccess; } -void Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, - bool sixaxis_status) { - if (!IsDeviceHandleValid(sixaxis_handle)) { - LOG_ERROR(Service_HID, "Invalid handle"); - return; +Result Controller_NPad::EnableSixAxisSensorUnalteredPassthrough( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + auto& sixaxis = GetSixaxisState(sixaxis_handle); + sixaxis.unaltered_passtrough = is_enabled; + return ResultSuccess; +} + +Result Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + is_enabled = sixaxis.unaltered_passtrough; + return ResultSuccess; +} + +Result Controller_NPad::LoadSixAxisSensorCalibrationParameter( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorCalibrationParameter& calibration) const { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + // TODO: Request this data to the controller. On error return 0xd8ca + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + calibration = sixaxis.calibration; + return ResultSuccess; +} + +Result Controller_NPad::GetSixAxisSensorIcInformation( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorIcInformation& ic_information) const { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + // TODO: Request this data to the controller. On error return 0xd8ca + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + ic_information = sixaxis.ic_information; + return ResultSuccess; +} + +Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned( + const Core::HID::SixAxisSensorHandle& sixaxis_handle) { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; } + + auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle); + sixaxis_properties.is_newly_assigned.Assign(0); + + return ResultSuccess; +} + +Result Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool sixaxis_status) { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + auto& controller = GetControllerFromHandle(sixaxis_handle); controller.sixaxis_sensor_enabled = sixaxis_status; + return ResultSuccess; } -void Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, - bool sixaxis_fusion_status) { - if (!IsDeviceHandleValid(sixaxis_handle)) { - LOG_ERROR(Service_HID, "Invalid handle"); - return; +Result Controller_NPad::IsSixAxisSensorFusionEnabled( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; } - auto& controller = GetControllerFromHandle(sixaxis_handle); - controller.sixaxis_fusion_enabled = sixaxis_fusion_status; + + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + is_fusion_enabled = sixaxis.is_fusion_enabled; + + return ResultSuccess; +} +Result Controller_NPad::SetSixAxisFusionEnabled( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + auto& sixaxis = GetSixaxisState(sixaxis_handle); + sixaxis.is_fusion_enabled = is_fusion_enabled; + + return ResultSuccess; } -void Controller_NPad::SetSixAxisFusionParameters( - Core::HID::SixAxisSensorHandle sixaxis_handle, +Result Controller_NPad::SetSixAxisFusionParameters( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) { - if (!IsDeviceHandleValid(sixaxis_handle)) { - LOG_ERROR(Service_HID, "Invalid handle"); - return; + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; } - auto& controller = GetControllerFromHandle(sixaxis_handle); - controller.sixaxis_fusion = sixaxis_fusion_parameters; -} -Core::HID::SixAxisSensorFusionParameters Controller_NPad::GetSixAxisFusionParameters( - Core::HID::SixAxisSensorHandle sixaxis_handle) { - if (!IsDeviceHandleValid(sixaxis_handle)) { - LOG_ERROR(Service_HID, "Invalid handle"); - // Since these parameters are unknow just return zeros - return {}; + const auto param1 = sixaxis_fusion_parameters.parameter1; + if (param1 < 0.0f || param1 > 1.0f) { + return InvalidSixAxisFusionRange; } - auto& controller = GetControllerFromHandle(sixaxis_handle); - return controller.sixaxis_fusion; + + auto& sixaxis = GetSixaxisState(sixaxis_handle); + sixaxis.fusion = sixaxis_fusion_parameters; + + return ResultSuccess; } -void Controller_NPad::ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle) { - if (!IsDeviceHandleValid(sixaxis_handle)) { - LOG_ERROR(Service_HID, "Invalid handle"); - return; +Result Controller_NPad::GetSixAxisFusionParameters( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorFusionParameters& parameters) const { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; } - auto& controller = GetControllerFromHandle(sixaxis_handle); - // Since these parameters are unknow just fill with zeros - controller.sixaxis_fusion = {}; + + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + parameters = sixaxis.fusion; + + return ResultSuccess; } -void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, - Core::HID::NpadIdType npad_id_2) { +Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, + Core::HID::NpadIdType npad_id_2) { if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, npad_id_2); - return; + return InvalidNpadId; } auto& controller_1 = GetControllerFromNpadIdType(npad_id_1); auto& controller_2 = GetControllerFromNpadIdType(npad_id_2); - const auto controller_style_1 = controller_1.device->GetNpadStyleIndex(); - const auto controller_style_2 = controller_2.device->GetNpadStyleIndex(); - bool merge_controllers = false; + auto controller_style_1 = controller_1.device->GetNpadStyleIndex(); + auto controller_style_2 = controller_2.device->GetNpadStyleIndex(); + + // Simplify this code by converting dualjoycon with only a side connected to single joycons + if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual) { + if (controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) { + controller_style_1 = Core::HID::NpadStyleIndex::JoyconLeft; + } + if (!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) { + controller_style_1 = Core::HID::NpadStyleIndex::JoyconRight; + } + } + if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) { + if (controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) { + controller_style_2 = Core::HID::NpadStyleIndex::JoyconLeft; + } + if (!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) { + controller_style_2 = Core::HID::NpadStyleIndex::JoyconRight; + } + } - // If the controllers at both npad indices form a pair of left and right joycons, merge them. - // Otherwise, do nothing. + // Invalid merge errors + if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual || + controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) { + return NpadIsDualJoycon; + } if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft && + controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft) { + return NpadIsSameType; + } + if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight && controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) { - merge_controllers = true; - } - if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft && - controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight) { - merge_controllers = true; - } - if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual && - controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight && - controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) { - merge_controllers = true; - } - if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual && - controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft && - !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) { - merge_controllers = true; - } - if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual && - controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight && - controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) { - merge_controllers = true; - } - if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual && - controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft && - !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) { - merge_controllers = true; - } - if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual && - controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual && - controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected && - !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) { - merge_controllers = true; - } - if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual && - controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual && - !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected && - controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) { - merge_controllers = true; - } - - if (merge_controllers) { - // Disconnect the joycon at the second id and connect the dual joycon at the first index. - DisconnectNpad(npad_id_2); - controller_1.is_dual_left_connected = true; - controller_1.is_dual_right_connected = true; - AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1); - return; + return NpadIsSameType; + } + + // These exceptions are handled as if they where dual joycon + if (controller_style_1 != Core::HID::NpadStyleIndex::JoyconLeft && + controller_style_1 != Core::HID::NpadStyleIndex::JoyconRight) { + return NpadIsDualJoycon; + } + if (controller_style_2 != Core::HID::NpadStyleIndex::JoyconLeft && + controller_style_2 != Core::HID::NpadStyleIndex::JoyconRight) { + return NpadIsDualJoycon; } - LOG_WARNING(Service_HID, - "Controllers can't be merged npad_id_1:{}, npad_id_2:{}, type_1:{}, type_2:{}, " - "dual_1(left/right):{}/{}, dual_2(left/right):{}/{}", - npad_id_1, npad_id_2, controller_1.device->GetNpadStyleIndex(), - controller_2.device->GetNpadStyleIndex(), controller_1.is_dual_left_connected, - controller_1.is_dual_right_connected, controller_2.is_dual_left_connected, - controller_2.is_dual_right_connected); + + // Disconnect the joycon at the second id and connect the dual joycon at the first index. + DisconnectNpad(npad_id_2); + controller_1.is_dual_left_connected = true; + controller_1.is_dual_right_connected = true; + AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1); + return ResultSuccess; } void Controller_NPad::StartLRAssignmentMode() { @@ -1167,17 +1373,17 @@ void Controller_NPad::StopLRAssignmentMode() { is_in_lr_assignment_mode = false; } -bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, - Core::HID::NpadIdType npad_id_2) { +Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, + Core::HID::NpadIdType npad_id_2) { if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, npad_id_2); - return false; + return InvalidNpadId; } if (npad_id_1 == Core::HID::NpadIdType::Handheld || npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other || npad_id_2 == Core::HID::NpadIdType::Other) { - return true; + return ResultSuccess; } const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device; const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device; @@ -1187,46 +1393,49 @@ bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, const auto is_connected_2 = controller_2->IsConnected(); if (!IsControllerSupported(type_index_1) && is_connected_1) { - return false; + return NpadNotConnected; } if (!IsControllerSupported(type_index_2) && is_connected_2) { - return false; + return NpadNotConnected; } UpdateControllerAt(type_index_2, npad_id_1, is_connected_2); UpdateControllerAt(type_index_1, npad_id_2, is_connected_1); - return true; + return ResultSuccess; } -Core::HID::LedPattern Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id) { +Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id, + Core::HID::LedPattern& pattern) const { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - return Core::HID::LedPattern{0, 0, 0, 0}; + return InvalidNpadId; } const auto& controller = GetControllerFromNpadIdType(npad_id).device; - return controller->GetLedPattern(); + pattern = controller->GetLedPattern(); + return ResultSuccess; } -bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled( - Core::HID::NpadIdType npad_id) const { +Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, + bool& is_valid) const { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - // Return the default value - return false; + return InvalidNpadId; } const auto& controller = GetControllerFromNpadIdType(npad_id); - return controller.unintended_home_button_input_protection; + is_valid = controller.unintended_home_button_input_protection; + return ResultSuccess; } -void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, - Core::HID::NpadIdType npad_id) { +Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled( + bool is_protection_enabled, Core::HID::NpadIdType npad_id) { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - return; + return InvalidNpadId; } auto& controller = GetControllerFromNpadIdType(npad_id); controller.unintended_home_button_input_protection = is_protection_enabled; + return ResultSuccess; } void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { @@ -1364,4 +1573,96 @@ const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpa return controller_data[npad_index]; } +Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( + const Core::HID::SixAxisSensorHandle& sixaxis_handle) { + auto& controller = GetControllerFromHandle(sixaxis_handle); + switch (sixaxis_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Pokeball: + return controller.shared_memory->sixaxis_fullkey_properties; + case Core::HID::NpadStyleIndex::Handheld: + return controller.shared_memory->sixaxis_handheld_properties; + case Core::HID::NpadStyleIndex::JoyconDual: + if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { + return controller.shared_memory->sixaxis_dual_left_properties; + } + return controller.shared_memory->sixaxis_dual_right_properties; + case Core::HID::NpadStyleIndex::JoyconLeft: + return controller.shared_memory->sixaxis_left_properties; + case Core::HID::NpadStyleIndex::JoyconRight: + return controller.shared_memory->sixaxis_right_properties; + default: + return controller.shared_memory->sixaxis_fullkey_properties; + } +} + +const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( + const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { + const auto& controller = GetControllerFromHandle(sixaxis_handle); + switch (sixaxis_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Pokeball: + return controller.shared_memory->sixaxis_fullkey_properties; + case Core::HID::NpadStyleIndex::Handheld: + return controller.shared_memory->sixaxis_handheld_properties; + case Core::HID::NpadStyleIndex::JoyconDual: + if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { + return controller.shared_memory->sixaxis_dual_left_properties; + } + return controller.shared_memory->sixaxis_dual_right_properties; + case Core::HID::NpadStyleIndex::JoyconLeft: + return controller.shared_memory->sixaxis_left_properties; + case Core::HID::NpadStyleIndex::JoyconRight: + return controller.shared_memory->sixaxis_right_properties; + default: + return controller.shared_memory->sixaxis_fullkey_properties; + } +} + +Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( + const Core::HID::SixAxisSensorHandle& sixaxis_handle) { + auto& controller = GetControllerFromHandle(sixaxis_handle); + switch (sixaxis_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Pokeball: + return controller.sixaxis_fullkey; + case Core::HID::NpadStyleIndex::Handheld: + return controller.sixaxis_handheld; + case Core::HID::NpadStyleIndex::JoyconDual: + if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { + return controller.sixaxis_dual_left; + } + return controller.sixaxis_dual_right; + case Core::HID::NpadStyleIndex::JoyconLeft: + return controller.sixaxis_left; + case Core::HID::NpadStyleIndex::JoyconRight: + return controller.sixaxis_right; + default: + return controller.sixaxis_unknown; + } +} + +const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( + const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { + const auto& controller = GetControllerFromHandle(sixaxis_handle); + switch (sixaxis_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Pokeball: + return controller.sixaxis_fullkey; + case Core::HID::NpadStyleIndex::Handheld: + return controller.sixaxis_handheld; + case Core::HID::NpadStyleIndex::JoyconDual: + if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { + return controller.sixaxis_dual_left; + } + return controller.sixaxis_dual_right; + case Core::HID::NpadStyleIndex::JoyconLeft: + return controller.sixaxis_left; + case Core::HID::NpadStyleIndex::JoyconRight: + return controller.sixaxis_right; + default: + return controller.sixaxis_unknown; + } +} + } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 6b2872bad..1a589cca2 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,7 +9,8 @@ #include "common/bit_field.h" #include "common/common_types.h" -#include "common/quaternion.h" +#include "common/vector_math.h" + #include "core/hid/hid_types.h" #include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/ring_lifo.h" @@ -27,13 +27,15 @@ class KReadableEvent; namespace Service::KernelHelpers { class ServiceContext; -} +} // namespace Service::KernelHelpers + +union Result; namespace Service::HID { class Controller_NPad final : public ControllerBase { public: - explicit Controller_NPad(Core::HID::HIDCore& hid_core_, + explicit Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, KernelHelpers::ServiceContext& service_context_); ~Controller_NPad() override; @@ -44,11 +46,10 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; // When the controller is requesting a motion update for the shared memory - void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) override; + void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) override; // This is nn::hid::GyroscopeZeroDriftMode enum class GyroscopeZeroDriftMode : u32 { @@ -80,6 +81,7 @@ public: Dual = 0, Single = 1, None = 2, + MaxActivationMode = 3, }; // This is nn::hid::NpadCommunicationMode @@ -106,8 +108,8 @@ public: void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_); NpadCommunicationMode GetNpadCommunicationMode() const; - void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type, - NpadJoyAssignmentMode assignment_mode); + Result SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type, + NpadJoyAssignmentMode assignment_mode); bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index, const Core::HID::VibrationValue& vibration_value); @@ -140,46 +142,68 @@ public: void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id, bool connected); - void DisconnectNpad(Core::HID::NpadIdType npad_id); - - void SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, - GyroscopeZeroDriftMode drift_mode); - GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode( - Core::HID::SixAxisSensorHandle sixaxis_handle) const; - bool IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const; - void SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, bool sixaxis_status); - void SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, - bool sixaxis_fusion_status); - void SetSixAxisFusionParameters( - Core::HID::SixAxisSensorHandle sixaxis_handle, + Result DisconnectNpad(Core::HID::NpadIdType npad_id); + + Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + GyroscopeZeroDriftMode drift_mode); + Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + GyroscopeZeroDriftMode& drift_mode) const; + Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool& is_at_rest) const; + Result IsFirmwareUpdateAvailableForSixAxisSensor( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const; + Result EnableSixAxisSensorUnalteredPassthrough( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled); + Result IsSixAxisSensorUnalteredPassthroughEnabled( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const; + Result LoadSixAxisSensorCalibrationParameter( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorCalibrationParameter& calibration) const; + Result GetSixAxisSensorIcInformation( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorIcInformation& ic_information) const; + Result ResetIsSixAxisSensorDeviceNewlyAssigned( + const Core::HID::SixAxisSensorHandle& sixaxis_handle); + Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool sixaxis_status); + Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool& is_fusion_enabled) const; + Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool is_fusion_enabled); + Result SetSixAxisFusionParameters( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters); - Core::HID::SixAxisSensorFusionParameters GetSixAxisFusionParameters( - Core::HID::SixAxisSensorHandle sixaxis_handle); - void ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle); - Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id); - bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const; - void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, - Core::HID::NpadIdType npad_id); + Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorFusionParameters& parameters) const; + Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const; + Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, + bool& is_enabled) const; + Result SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, + Core::HID::NpadIdType npad_id); void SetAnalogStickUseCenterClamp(bool use_center_clamp); void ClearAllConnectedControllers(); void DisconnectAllConnectedControllers(); void ConnectAllDisconnectedControllers(); void ClearAllControllers(); - void MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2); + Result MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, + Core::HID::NpadIdType npad_id_2); void StartLRAssignmentMode(); void StopLRAssignmentMode(); - bool SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2); + Result SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2); // Logical OR for all buttons presses on all controllers // Specifically for cheat engine and other features. Core::HID::NpadButton GetAndResetPressState(); static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); - static bool IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle); - static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); + static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); + static Result VerifyValidSixAxisSensorHandle( + const Core::HID::SixAxisSensorHandle& device_handle); private: + static constexpr std::size_t NPAD_COUNT = 10; + // This is nn::hid::detail::ColorAttribute enum class ColorAttribute : u32 { Ok = 0, @@ -190,16 +214,16 @@ private: // This is nn::hid::detail::NpadFullKeyColorState struct NpadFullKeyColorState { - ColorAttribute attribute; - Core::HID::NpadControllerColor fullkey; + ColorAttribute attribute{ColorAttribute::NoController}; + Core::HID::NpadControllerColor fullkey{}; }; static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size"); // This is nn::hid::detail::NpadJoyColorState struct NpadJoyColorState { - ColorAttribute attribute; - Core::HID::NpadControllerColor left; - Core::HID::NpadControllerColor right; + ColorAttribute attribute{ColorAttribute::NoController}; + Core::HID::NpadControllerColor left{}; + Core::HID::NpadControllerColor right{}; }; static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size"); @@ -225,11 +249,11 @@ private: // This is nn::hid::NpadPalmaState // This is nn::hid::NpadSystemExtState struct NPadGenericState { - s64_le sampling_number; - Core::HID::NpadButtonState npad_buttons; - Core::HID::AnalogStickState l_stick; - Core::HID::AnalogStickState r_stick; - NpadAttribute connection_status; + s64_le sampling_number{}; + Core::HID::NpadButtonState npad_buttons{}; + Core::HID::AnalogStickState l_stick{}; + Core::HID::AnalogStickState r_stick{}; + NpadAttribute connection_status{}; INSERT_PADDING_BYTES(4); // Reserved }; static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); @@ -252,7 +276,7 @@ private: Common::Vec3f gyro{}; Common::Vec3f rotation{}; std::array<Common::Vec3f, 3> orientation{}; - SixAxisSensorAttribute attribute; + SixAxisSensorAttribute attribute{}; INSERT_PADDING_BYTES(4); // Reserved }; static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); @@ -324,11 +348,11 @@ private: // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl struct NfcXcdDeviceHandleStateImpl { - u64 handle; - bool is_available; - bool is_activated; + u64 handle{}; + bool is_available{}; + bool is_activated{}; INSERT_PADDING_BYTES(0x6); // Reserved - u64 sampling_number; + u64 sampling_number{}; }; static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, "NfcXcdDeviceHandleStateImpl is an invalid size"); @@ -365,8 +389,8 @@ private: }; struct AppletFooterUi { - AppletFooterUiAttributes attributes; - AppletFooterUiType type; + AppletFooterUiAttributes attributes{}; + AppletFooterUiType type{AppletFooterUiType::None}; INSERT_PADDING_BYTES(0x5B); // Reserved }; static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size"); @@ -401,46 +425,54 @@ private: U, }; + struct AppletNfcXcd { + union { + AppletFooterUi applet_footer{}; + Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo; + }; + }; + // This is nn::hid::detail::NpadInternalState struct NpadInternalState { - Core::HID::NpadStyleTag style_tag; - NpadJoyAssignmentMode assignment_mode; - NpadFullKeyColorState fullkey_color; - NpadJoyColorState joycon_color; - Lifo<NPadGenericState, hid_entry_count> fullkey_lifo; - Lifo<NPadGenericState, hid_entry_count> handheld_lifo; - Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo; - Lifo<NPadGenericState, hid_entry_count> joy_left_lifo; - Lifo<NPadGenericState, hid_entry_count> joy_right_lifo; - Lifo<NPadGenericState, hid_entry_count> palma_lifo; - Lifo<NPadGenericState, hid_entry_count> system_ext_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo; - DeviceType device_type; + Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None}; + NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual}; + NpadFullKeyColorState fullkey_color{}; + NpadJoyColorState joycon_color{}; + Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{}; + Lifo<NPadGenericState, hid_entry_count> handheld_lifo{}; + Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{}; + Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{}; + Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{}; + Lifo<NPadGenericState, hid_entry_count> palma_lifo{}; + Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{}; + DeviceType device_type{}; INSERT_PADDING_BYTES(0x4); // Reserved - NPadSystemProperties system_properties; - NpadSystemButtonProperties button_properties; - Core::HID::NpadBatteryLevel battery_level_dual; - Core::HID::NpadBatteryLevel battery_level_left; - Core::HID::NpadBatteryLevel battery_level_right; - union { - Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{}; - AppletFooterUi applet_footer; - }; + NPadSystemProperties system_properties{}; + NpadSystemButtonProperties button_properties{}; + Core::HID::NpadBatteryLevel battery_level_dual{}; + Core::HID::NpadBatteryLevel battery_level_left{}; + Core::HID::NpadBatteryLevel battery_level_right{}; + AppletNfcXcd applet_nfc_xcd{}; INSERT_PADDING_BYTES(0x20); // Unknown - Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo; - NpadLarkType lark_type_l_and_main; - NpadLarkType lark_type_r; - NpadLuciaType lucia_type; - NpadLagonType lagon_type; - NpadLagerType lager_type; - // FW 13.x Investigate there is some sort of bitflag related to joycons - INSERT_PADDING_BYTES(0x4); - INSERT_PADDING_BYTES(0xc08); // Unknown + Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{}; + NpadLarkType lark_type_l_and_main{}; + NpadLarkType lark_type_r{}; + NpadLuciaType lucia_type{}; + NpadLagonType lagon_type{}; + NpadLagerType lager_type{}; + Core::HID::SixAxisSensorProperties sixaxis_fullkey_properties; + Core::HID::SixAxisSensorProperties sixaxis_handheld_properties; + Core::HID::SixAxisSensorProperties sixaxis_dual_left_properties; + Core::HID::SixAxisSensorProperties sixaxis_dual_right_properties; + Core::HID::SixAxisSensorProperties sixaxis_left_properties; + Core::HID::SixAxisSensorProperties sixaxis_right_properties; + INSERT_PADDING_BYTES(0xc06); // Unknown }; static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size"); @@ -450,10 +482,19 @@ private: std::chrono::steady_clock::time_point last_vibration_timepoint{}; }; + struct SixaxisParameters { + bool is_fusion_enabled{true}; + bool unaltered_passtrough{false}; + Core::HID::SixAxisSensorFusionParameters fusion{}; + Core::HID::SixAxisSensorCalibrationParameter calibration{}; + Core::HID::SixAxisSensorIcInformation ic_information{}; + GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; + }; + struct NpadControllerData { - Core::HID::EmulatedController* device; Kernel::KEvent* styleset_changed_event{}; - NpadInternalState shared_memory_entry{}; + NpadInternalState* shared_memory = nullptr; + Core::HID::EmulatedController* device = nullptr; std::array<VibrationData, 2> vibration{}; bool unintended_home_button_input_protection{}; @@ -466,9 +507,13 @@ private: // Motion parameters bool sixaxis_at_rest{true}; bool sixaxis_sensor_enabled{true}; - bool sixaxis_fusion_enabled{false}; - Core::HID::SixAxisSensorFusionParameters sixaxis_fusion{}; - GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; + SixaxisParameters sixaxis_fullkey{}; + SixaxisParameters sixaxis_handheld{}; + SixaxisParameters sixaxis_dual_left{}; + SixaxisParameters sixaxis_dual_right{}; + SixaxisParameters sixaxis_left{}; + SixaxisParameters sixaxis_right{}; + SixaxisParameters sixaxis_unknown{}; // Current pad state NPadGenericState npad_pad_state{}; @@ -480,14 +525,14 @@ private: SixAxisSensorState sixaxis_dual_right_state{}; SixAxisSensorState sixaxis_left_lifo_state{}; SixAxisSensorState sixaxis_right_lifo_state{}; - int callback_key; + int callback_key{}; }; void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx); void InitNewlyAddedController(Core::HID::NpadIdType npad_id); bool IsControllerSupported(Core::HID::NpadStyleIndex controller) const; void RequestPadStateUpdate(Core::HID::NpadIdType npad_id); - void WriteEmptyEntry(NpadInternalState& npad); + void WriteEmptyEntry(NpadInternalState* npad); NpadControllerData& GetControllerFromHandle( const Core::HID::SixAxisSensorHandle& device_handle); @@ -500,9 +545,17 @@ private: NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; + Core::HID::SixAxisSensorProperties& GetSixaxisProperties( + const Core::HID::SixAxisSensorHandle& device_handle); + const Core::HID::SixAxisSensorProperties& GetSixaxisProperties( + const Core::HID::SixAxisSensorHandle& device_handle) const; + SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle); + const SixaxisParameters& GetSixaxisState( + const Core::HID::SixAxisSensorHandle& device_handle) const; + std::atomic<u64> press_state{}; - std::array<NpadControllerData, 10> controller_data{}; + std::array<NpadControllerData, NPAD_COUNT> controller_data{}; KernelHelpers::ServiceContext& service_context; std::mutex mutex; std::vector<Core::HID::NpadIdType> supported_npad_id_types{}; @@ -510,7 +563,8 @@ private: NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; bool permit_vibration_session_enabled{false}; - bool analog_stick_use_center_clamp{}; + bool analog_stick_use_center_clamp{false}; bool is_in_lr_assignment_mode{false}; + bool is_controller_initialized{false}; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp new file mode 100644 index 000000000..575d4e626 --- /dev/null +++ b/src/core/hle/service/hid/controllers/palma.cpp @@ -0,0 +1,229 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core_timing.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hid/hid_types.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/service/hid/controllers/palma.h" +#include "core/hle/service/kernel_helpers.h" + +namespace Service::HID { + +Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, + KernelHelpers::ServiceContext& service_context_) + : ControllerBase{hid_core_}, service_context{service_context_} { + controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); + operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); +} + +Controller_Palma::~Controller_Palma() = default; + +void Controller_Palma::OnInit() {} + +void Controller_Palma::OnRelease() {} + +void Controller_Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) { + if (!IsControllerActivated()) { + return; + } +} + +Result Controller_Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, + PalmaConnectionHandle& handle) { + active_handle.npad_id = npad_id; + handle = active_handle; + return ResultSuccess; +} + +Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + ActivateController(); + return ResultSuccess; +} + +Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent( + const PalmaConnectionHandle& handle) const { + if (handle.npad_id != active_handle.npad_id) { + LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id); + } + return operation_complete_event->GetReadableEvent(); +} + +Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle, + PalmaOperationType& operation_type, + PalmaOperationData& data) const { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation_type = operation.operation; + data = operation.data; + return ResultSuccess; +} + +Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, + u64 palma_activity) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::PlayActivity; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, + PalmaFrModeType fr_mode_) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + fr_mode = fr_mode_; + return ResultSuccess; +} + +Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::ReadStep; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + return ResultSuccess; +} + +Result Controller_Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + return ResultSuccess; +} + +void Controller_Palma::ReadPalmaApplicationSection() {} + +void Controller_Palma::WritePalmaApplicationSection() {} + +Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::ReadUniqueCode; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::SetUniqueCodeInvalid; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +void Controller_Palma::WritePalmaActivityEntry() {} + +Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, + u64 unknown) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::WriteRgbLedPatternEntry; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, + u8* t_mem, u64 size) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::WriteWaveEntry; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, + s32 database_id_version_) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + database_id_version = database_id_version_; + operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion; + operation.result = PalmaResultSuccess; + operation.data[0] = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::GetPalmaDataBaseIdentificationVersion( + const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation.data[0] = static_cast<u8>(database_id_version); + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +void Controller_Palma::SuspendPalmaFeature() {} + +Result Controller_Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + return operation.result; +} +void Controller_Palma::ReadPalmaPlayLog() {} + +void Controller_Palma::ResetPalmaPlayLog() {} + +void Controller_Palma::SetIsPalmaAllConnectable(bool is_all_connectable) { + // If true controllers are able to be paired + is_connectable = is_all_connectable; +} + +void Controller_Palma::SetIsPalmaPairedConnectable() {} + +Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + // TODO: Do something + return ResultSuccess; +} + +void Controller_Palma::SetPalmaBoostMode(bool boost_mode) {} + +void Controller_Palma::CancelWritePalmaWaveEntry() {} + +void Controller_Palma::EnablePalmaBoostMode() {} + +void Controller_Palma::GetPalmaBluetoothAddress() {} + +void Controller_Palma::SetDisallowedPalmaConnection() {} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/palma.h b/src/core/hle/service/hid/controllers/palma.h new file mode 100644 index 000000000..1d7fc94e1 --- /dev/null +++ b/src/core/hle/service/hid/controllers/palma.h @@ -0,0 +1,163 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/service/hid/controllers/controller_base.h" +#include "core/hle/service/hid/errors.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Service::KernelHelpers { +class ServiceContext; +} + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::HID { +class Controller_Palma final : public ControllerBase { +public: + using PalmaOperationData = std::array<u8, 0x140>; + + // This is nn::hid::PalmaOperationType + enum class PalmaOperationType { + PlayActivity, + SetFrModeType, + ReadStep, + EnableStep, + ResetStep, + ReadApplicationSection, + WriteApplicationSection, + ReadUniqueCode, + SetUniqueCodeInvalid, + WriteActivityEntry, + WriteRgbLedPatternEntry, + WriteWaveEntry, + ReadDataBaseIdentificationVersion, + WriteDataBaseIdentificationVersion, + SuspendFeature, + ReadPlayLog, + ResetPlayLog, + }; + + // This is nn::hid::PalmaWaveSet + enum class PalmaWaveSet : u64 { + Small, + Medium, + Large, + }; + + // This is nn::hid::PalmaFrModeType + enum class PalmaFrModeType : u64 { + Off, + B01, + B02, + B03, + Downloaded, + }; + + // This is nn::hid::PalmaFeature + enum class PalmaFeature : u64 { + FrMode, + RumbleFeedback, + Step, + MuteSwitch, + }; + + // This is nn::hid::PalmaOperationInfo + struct PalmaOperationInfo { + PalmaOperationType operation{}; + Result result{PalmaResultSuccess}; + PalmaOperationData data{}; + }; + static_assert(sizeof(PalmaOperationInfo) == 0x148, "PalmaOperationInfo is an invalid size"); + + // This is nn::hid::PalmaActivityEntry + struct PalmaActivityEntry { + u32 rgb_led_pattern_index; + INSERT_PADDING_BYTES(2); + PalmaWaveSet wave_set; + u32 wave_index; + INSERT_PADDING_BYTES(12); + }; + static_assert(sizeof(PalmaActivityEntry) == 0x20, "PalmaActivityEntry is an invalid size"); + + struct PalmaConnectionHandle { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_BYTES(4); // Unknown + }; + static_assert(sizeof(PalmaConnectionHandle) == 0x8, + "PalmaConnectionHandle has incorrect size."); + + explicit Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, + KernelHelpers::ServiceContext& service_context_); + ~Controller_Palma() override; + + // Called when the controller is initialized + void OnInit() override; + + // When the controller is released + void OnRelease() override; + + // When the controller is requesting an update for the shared memory + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; + + Result GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, PalmaConnectionHandle& handle); + Result InitializePalma(const PalmaConnectionHandle& handle); + Kernel::KReadableEvent& AcquirePalmaOperationCompleteEvent( + const PalmaConnectionHandle& handle) const; + Result GetPalmaOperationInfo(const PalmaConnectionHandle& handle, + PalmaOperationType& operation_type, + PalmaOperationData& data) const; + Result PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity); + Result SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_); + Result ReadPalmaStep(const PalmaConnectionHandle& handle); + Result EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled); + Result ResetPalmaStep(const PalmaConnectionHandle& handle); + Result ReadPalmaUniqueCode(const PalmaConnectionHandle& handle); + Result SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle); + Result WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown); + Result WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, u8* t_mem, + u64 size); + Result SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, + s32 database_id_version_); + Result GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle); + Result GetPalmaOperationResult(const PalmaConnectionHandle& handle) const; + void SetIsPalmaAllConnectable(bool is_all_connectable); + Result PairPalma(const PalmaConnectionHandle& handle); + void SetPalmaBoostMode(bool boost_mode); + +private: + void ReadPalmaApplicationSection(); + void WritePalmaApplicationSection(); + void WritePalmaActivityEntry(); + void SuspendPalmaFeature(); + void ReadPalmaPlayLog(); + void ResetPalmaPlayLog(); + void SetIsPalmaPairedConnectable(); + void CancelWritePalmaWaveEntry(); + void EnablePalmaBoostMode(); + void GetPalmaBluetoothAddress(); + void SetDisallowedPalmaConnection(); + + bool is_connectable{}; + s32 database_id_version{}; + PalmaOperationInfo operation{}; + PalmaFrModeType fr_mode{}; + PalmaConnectionHandle active_handle{}; + + Core::HID::EmulatedController* controller; + + Kernel::KEvent* operation_complete_event; + KernelHelpers::ServiceContext& service_context; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp index b7d7a5756..df9ee0c3f 100644 --- a/src/core/hle/service/hid/controllers/stubbed.cpp +++ b/src/core/hle/service/hid/controllers/stubbed.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include "common/common_types.h" @@ -10,15 +9,18 @@ namespace Service::HID { -Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} +Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) + : ControllerBase{hid_core_} { + raw_shared_memory = raw_shared_memory_; +} + Controller_Stubbed::~Controller_Stubbed() = default; void Controller_Stubbed::OnInit() {} void Controller_Stubbed::OnRelease() {} -void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) { +void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!smart_update) { return; } @@ -29,7 +31,7 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u header.entry_count = 0; header.last_entry_index = 0; - std::memcpy(data + common_offset, &header, sizeof(CommonHeader)); + std::memcpy(raw_shared_memory + common_offset, &header, sizeof(CommonHeader)); } void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) { diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h index 0044a4efa..1483a968e 100644 --- a/src/core/hle/service/hid/controllers/stubbed.h +++ b/src/core/hle/service/hid/controllers/stubbed.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,7 +9,7 @@ namespace Service::HID { class Controller_Stubbed final : public ControllerBase { public: - explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_); + explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); ~Controller_Stubbed() override; // Called when the controller is initialized @@ -20,19 +19,20 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; void SetCommonHeaderOffset(std::size_t off); private: struct CommonHeader { - s64 timestamp; - s64 total_entry_count; - s64 last_entry_index; - s64 entry_count; + s64 timestamp{}; + s64 total_entry_count{}; + s64 last_entry_index{}; + s64 entry_count{}; }; static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size"); + u8* raw_shared_memory = nullptr; bool smart_update{}; std::size_t common_offset{}; }; diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 48978e5c6..1da8d3eb0 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -1,11 +1,9 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstring> #include "common/common_types.h" -#include "common/logging/log.h" #include "common/settings.h" #include "core/core.h" #include "core/core_timing.h" @@ -17,8 +15,13 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; -Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_) +Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_, + u8* raw_shared_memory_) : ControllerBase{hid_core_} { + static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size, + "TouchSharedMemory is bigger than the shared memory"); + shared_memory = std::construct_at( + reinterpret_cast<TouchSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); console = hid_core.GetEmulatedConsole(); } @@ -28,14 +31,12 @@ void Controller_Touchscreen::OnInit() {} void Controller_Touchscreen::OnRelease() {} -void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) { - touch_screen_lifo.timestamp = core_timing.GetCPUTicks(); +void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { + shared_memory->touch_screen_lifo.timestamp = core_timing.GetCPUTicks(); if (!IsControllerActivated()) { - touch_screen_lifo.buffer_count = 0; - touch_screen_lifo.buffer_tail = 0; - std::memcpy(data, &touch_screen_lifo, sizeof(touch_screen_lifo)); + shared_memory->touch_screen_lifo.buffer_count = 0; + shared_memory->touch_screen_lifo.buffer_tail = 0; return; } @@ -43,7 +44,6 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin for (std::size_t id = 0; id < MAX_FINGERS; id++) { const auto& current_touch = touch_status[id]; auto& finger = fingers[id]; - finger.position = current_touch.position; finger.id = current_touch.id; if (finger.attribute.start_touch) { @@ -60,13 +60,18 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin if (!finger.pressed && current_touch.pressed) { finger.attribute.start_touch.Assign(1); finger.pressed = true; + finger.position = current_touch.position; continue; } if (finger.pressed && !current_touch.pressed) { finger.attribute.raw = 0; finger.attribute.end_touch.Assign(1); + continue; } + + // Only update position if touch is not on a special frame + finger.position = current_touch.position; } std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers; @@ -76,7 +81,7 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); const u64 tick = core_timing.GetCPUTicks(); - const auto& last_entry = touch_screen_lifo.ReadCurrentEntry().state; + const auto& last_entry = shared_memory->touch_screen_lifo.ReadCurrentEntry().state; next_state.sampling_number = last_entry.sampling_number + 1; next_state.entry_count = static_cast<s32>(active_fingers_count); @@ -108,8 +113,7 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin } } - touch_screen_lifo.WriteNextEntry(next_state); - std::memcpy(data + SHARED_MEMORY_OFFSET, &touch_screen_lifo, sizeof(touch_screen_lifo)); + shared_memory->touch_screen_lifo.WriteNextEntry(next_state); } } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index 708dde4f0..e57a3a80e 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h @@ -1,14 +1,10 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" -#include "common/point.h" -#include "common/swap.h" #include "core/hid/hid_types.h" #include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/ring_lifo.h" @@ -29,14 +25,14 @@ public: // This is nn::hid::TouchScreenConfigurationForNx struct TouchScreenConfigurationForNx { - TouchScreenModeForNx mode; + TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting}; INSERT_PADDING_BYTES_NOINIT(0x7); INSERT_PADDING_BYTES_NOINIT(0xF); // Reserved }; static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17, "TouchScreenConfigurationForNx is an invalid size"); - explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_); + explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); ~Controller_Touchscreen() override; // Called when the controller is initialized @@ -46,26 +42,32 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; private: static constexpr std::size_t MAX_FINGERS = 16; // This is nn::hid::TouchScreenState struct TouchScreenState { - s64 sampling_number; - s32 entry_count; + s64 sampling_number{}; + s32 entry_count{}; INSERT_PADDING_BYTES(4); // Reserved - std::array<Core::HID::TouchState, MAX_FINGERS> states; + std::array<Core::HID::TouchState, MAX_FINGERS> states{}; }; static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size"); - // This is nn::hid::detail::TouchScreenLifo - Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{}; - static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size"); + struct TouchSharedMemory { + // This is nn::hid::detail::TouchScreenLifo + Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{}; + static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size"); + INSERT_PADDING_WORDS(0xF2); + }; + static_assert(sizeof(TouchSharedMemory) == 0x3000, "TouchSharedMemory is an invalid size"); + TouchScreenState next_state{}; + TouchSharedMemory* shared_memory = nullptr; + Core::HID::EmulatedConsole* console = nullptr; - std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers; - Core::HID::EmulatedConsole* console; + std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{}; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp index e4da16466..62119e2c5 100644 --- a/src/core/hle/service/hid/controllers/xpad.cpp +++ b/src/core/hle/service/hid/controllers/xpad.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include "common/common_types.h" @@ -11,28 +10,31 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; -Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} +Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) + : ControllerBase{hid_core_} { + static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size, + "XpadSharedMemory is bigger than the shared memory"); + shared_memory = std::construct_at( + reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); +} Controller_XPad::~Controller_XPad() = default; void Controller_XPad::OnInit() {} void Controller_XPad::OnRelease() {} -void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) { +void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { - basic_xpad_lifo.buffer_count = 0; - basic_xpad_lifo.buffer_tail = 0; - std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo)); + shared_memory->basic_xpad_lifo.buffer_count = 0; + shared_memory->basic_xpad_lifo.buffer_tail = 0; return; } - const auto& last_entry = basic_xpad_lifo.ReadCurrentEntry().state; + const auto& last_entry = shared_memory->basic_xpad_lifo.ReadCurrentEntry().state; next_state.sampling_number = last_entry.sampling_number + 1; // TODO(ogniK): Update xpad states - basic_xpad_lifo.WriteNextEntry(next_state); - std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo)); + shared_memory->basic_xpad_lifo.WriteNextEntry(next_state); } } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h index ba8db8d9d..d01dee5fc 100644 --- a/src/core/hle/service/hid/controllers/xpad.h +++ b/src/core/hle/service/hid/controllers/xpad.h @@ -1,13 +1,10 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include "common/bit_field.h" -#include "common/common_funcs.h" #include "common/common_types.h" -#include "common/swap.h" #include "core/hid/hid_types.h" #include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/ring_lifo.h" @@ -15,7 +12,7 @@ namespace Service::HID { class Controller_XPad final : public ControllerBase { public: - explicit Controller_XPad(Core::HID::HIDCore& hid_core_); + explicit Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); ~Controller_XPad() override; // Called when the controller is initialized @@ -25,7 +22,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; private: // This is nn::hid::BasicXpadAttributeSet @@ -93,17 +90,23 @@ private: // This is nn::hid::detail::BasicXpadState struct BasicXpadState { - s64 sampling_number; - BasicXpadAttributeSet attributes; - BasicXpadButtonSet pad_states; - Core::HID::AnalogStickState l_stick; - Core::HID::AnalogStickState r_stick; + s64 sampling_number{}; + BasicXpadAttributeSet attributes{}; + BasicXpadButtonSet pad_states{}; + Core::HID::AnalogStickState l_stick{}; + Core::HID::AnalogStickState r_stick{}; }; static_assert(sizeof(BasicXpadState) == 0x20, "BasicXpadState is an invalid size"); - // This is nn::hid::detail::BasicXpadLifo - Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{}; - static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size"); + struct XpadSharedMemory { + // This is nn::hid::detail::BasicXpadLifo + Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{}; + static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size"); + INSERT_PADDING_WORDS(0x4E); + }; + static_assert(sizeof(XpadSharedMemory) == 0x400, "XpadSharedMemory is an invalid size"); + BasicXpadState next_state{}; + XpadSharedMemory* shared_memory = nullptr; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h index 3583642e7..76208e9a4 100644 --- a/src/core/hle/service/hid/errors.h +++ b/src/core/hle/service/hid/errors.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,6 +7,24 @@ namespace Service::HID { -constexpr ResultCode ERR_NPAD_NOT_CONNECTED{ErrorModule::HID, 710}; +constexpr Result PalmaResultSuccess{ErrorModule::HID, 0}; +constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; +constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; +constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122}; +constexpr Result VibrationInvalidNpadId{ErrorModule::HID, 123}; +constexpr Result VibrationDeviceIndexOutOfRange{ErrorModule::HID, 124}; +constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423}; +constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601}; +constexpr Result NpadIsSameType{ErrorModule::HID, 602}; +constexpr Result InvalidNpadId{ErrorModule::HID, 709}; +constexpr Result NpadNotConnected{ErrorModule::HID, 710}; +constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302}; } // namespace Service::HID + +namespace Service::IRS { + +constexpr Result InvalidProcessorState{ErrorModule::Irsensor, 78}; +constexpr Result InvalidIrCameraHandle{ErrorModule::Irsensor, 204}; + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index d9202ea6c..46bad7871 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include "common/common_types.h" @@ -16,6 +15,7 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/service/hid/errors.h" #include "core/hle/service/hid/hid.h" +#include "core/hle/service/hid/hidbus.h" #include "core/hle/service/hid/irs.h" #include "core/hle/service/hid/xcd.h" #include "core/memory.h" @@ -27,6 +27,7 @@ #include "core/hle/service/hid/controllers/keyboard.h" #include "core/hle/service/hid/controllers/mouse.h" #include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/controllers/palma.h" #include "core/hle/service/hid/controllers/stubbed.h" #include "core/hle/service/hid/controllers/touchscreen.h" #include "core/hle/service/hid/controllers/xpad.h" @@ -35,11 +36,10 @@ namespace Service::HID { // Updating period for each HID device. // Period time is obtained by measuring the number of samples in a second on HW using a homebrew -constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz) +// Correct pad_update_ns is 4ms this is overclocked to lower input lag +constexpr auto pad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz) constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) -// TODO: Correct update rate for motion is 5ms. Check why some games don't behave at that speed -constexpr auto motion_update_ns = std::chrono::nanoseconds{10 * 1000 * 1000}; // (10ms, 100Hz) -constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; +constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) IAppletResource::IAppletResource(Core::System& system_, KernelHelpers::ServiceContext& service_context_) @@ -48,20 +48,21 @@ IAppletResource::IAppletResource(Core::System& system_, {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, }; RegisterHandlers(functions); - - MakeController<Controller_DebugPad>(HidController::DebugPad); - MakeController<Controller_Touchscreen>(HidController::Touchscreen); - MakeController<Controller_Mouse>(HidController::Mouse); - MakeController<Controller_Keyboard>(HidController::Keyboard); - MakeController<Controller_XPad>(HidController::XPad); - MakeController<Controller_Stubbed>(HidController::HomeButton); - MakeController<Controller_Stubbed>(HidController::SleepButton); - MakeController<Controller_Stubbed>(HidController::CaptureButton); - MakeController<Controller_Stubbed>(HidController::InputDetector); - MakeController<Controller_Stubbed>(HidController::UniquePad); - MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad); - MakeController<Controller_Gesture>(HidController::Gesture); - MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor); + u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer(); + MakeController<Controller_DebugPad>(HidController::DebugPad, shared_memory); + MakeController<Controller_Touchscreen>(HidController::Touchscreen, shared_memory); + MakeController<Controller_Mouse>(HidController::Mouse, shared_memory); + MakeController<Controller_Keyboard>(HidController::Keyboard, shared_memory); + MakeController<Controller_XPad>(HidController::XPad, shared_memory); + MakeController<Controller_Stubbed>(HidController::HomeButton, shared_memory); + MakeController<Controller_Stubbed>(HidController::SleepButton, shared_memory); + MakeController<Controller_Stubbed>(HidController::CaptureButton, shared_memory); + MakeController<Controller_Stubbed>(HidController::InputDetector, shared_memory); + MakeController<Controller_Stubbed>(HidController::UniquePad, shared_memory); + MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad, shared_memory); + MakeController<Controller_Gesture>(HidController::Gesture, shared_memory); + MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor, shared_memory); + MakeControllerWithServiceContext<Controller_Palma>(HidController::Palma, shared_memory); // Homebrew doesn't try to activate some controllers, so we activate them by default GetController<Controller_NPad>(HidController::NPad).ActivateController(); @@ -76,26 +77,34 @@ IAppletResource::IAppletResource(Core::System& system_, // Register update callbacks pad_update_event = Core::Timing::CreateEvent( "HID::UpdatePadCallback", - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto guard = LockService(); UpdateControllers(user_data, ns_late); + return std::nullopt; }); mouse_keyboard_update_event = Core::Timing::CreateEvent( "HID::UpdateMouseKeyboardCallback", - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto guard = LockService(); UpdateMouseKeyboard(user_data, ns_late); + return std::nullopt; }); motion_update_event = Core::Timing::CreateEvent( "HID::UpdateMotionCallback", - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto guard = LockService(); UpdateMotion(user_data, ns_late); + return std::nullopt; }); - system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event); - system.CoreTiming().ScheduleEvent(mouse_keyboard_update_ns, mouse_keyboard_update_event); - system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event); + system.CoreTiming().ScheduleLoopingEvent(pad_update_ns, pad_update_ns, pad_update_event); + system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, + mouse_keyboard_update_event); + system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, + motion_update_event); system.HIDCore().ReloadInputDevices(); } @@ -135,47 +144,22 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data, if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) { continue; } - controller->OnUpdate(core_timing, system.Kernel().GetHidSharedMem().GetPointer(), - SHARED_MEMORY_SIZE); - } - - // If ns_late is higher than the update rate ignore the delay - if (ns_late > pad_update_ns) { - ns_late = {}; + controller->OnUpdate(core_timing); } - - core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event); } void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { auto& core_timing = system.CoreTiming(); - controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate( - core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE); - controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate( - core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE); - - // If ns_late is higher than the update rate ignore the delay - if (ns_late > mouse_keyboard_update_ns) { - ns_late = {}; - } - - core_timing.ScheduleEvent(mouse_keyboard_update_ns - ns_late, mouse_keyboard_update_event); + controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing); + controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing); } void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { auto& core_timing = system.CoreTiming(); - controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate( - core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE); - - // If ns_late is higher than the update rate ignore the delay - if (ns_late > motion_update_ns) { - ns_late = {}; - } - - core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event); + controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing); } class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { @@ -247,7 +231,7 @@ Hid::Hid(Core::System& system_) {65, nullptr, "GetJoySixAxisSensorLifoHandle"}, {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"}, - {68, nullptr, "IsSixAxisSensorFusionEnabled"}, + {68, &Hid::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"}, {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"}, {70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"}, {71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"}, @@ -263,12 +247,12 @@ Hid::Hid(Core::System& system_) {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"}, {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, {83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"}, - {84, nullptr, "EnableSixAxisSensorUnalteredPassthrough"}, - {85, nullptr, "IsSixAxisSensorUnalteredPassthroughEnabled"}, + {84, &Hid::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"}, + {85, &Hid::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"}, {86, nullptr, "StoreSixAxisSensorCalibrationParameter"}, - {87, nullptr, "LoadSixAxisSensorCalibrationParameter"}, - {88, nullptr, "GetSixAxisSensorIcInformation"}, - {89, nullptr, "ResetIsSixAxisSensorDeviceNewlyAssigned"}, + {87, &Hid::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"}, + {88, &Hid::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"}, + {89, &Hid::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"}, {91, &Hid::ActivateGesture, "ActivateGesture"}, {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, @@ -329,40 +313,40 @@ Hid::Hid(Core::System& system_) {406, nullptr, "GetNpadLeftRightInterfaceType"}, {407, nullptr, "GetNpadOfHighestBatteryLevel"}, {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"}, - {500, nullptr, "GetPalmaConnectionHandle"}, - {501, nullptr, "InitializePalma"}, - {502, nullptr, "AcquirePalmaOperationCompleteEvent"}, - {503, nullptr, "GetPalmaOperationInfo"}, - {504, nullptr, "PlayPalmaActivity"}, - {505, nullptr, "SetPalmaFrModeType"}, - {506, nullptr, "ReadPalmaStep"}, - {507, nullptr, "EnablePalmaStep"}, - {508, nullptr, "ResetPalmaStep"}, - {509, nullptr, "ReadPalmaApplicationSection"}, - {510, nullptr, "WritePalmaApplicationSection"}, - {511, nullptr, "ReadPalmaUniqueCode"}, - {512, nullptr, "SetPalmaUniqueCodeInvalid"}, - {513, nullptr, "WritePalmaActivityEntry"}, - {514, nullptr, "WritePalmaRgbLedPatternEntry"}, - {515, nullptr, "WritePalmaWaveEntry"}, - {516, nullptr, "SetPalmaDataBaseIdentificationVersion"}, - {517, nullptr, "GetPalmaDataBaseIdentificationVersion"}, - {518, nullptr, "SuspendPalmaFeature"}, - {519, nullptr, "GetPalmaOperationResult"}, - {520, nullptr, "ReadPalmaPlayLog"}, - {521, nullptr, "ResetPalmaPlayLog"}, + {500, &Hid::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"}, + {501, &Hid::InitializePalma, "InitializePalma"}, + {502, &Hid::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"}, + {503, &Hid::GetPalmaOperationInfo, "GetPalmaOperationInfo"}, + {504, &Hid::PlayPalmaActivity, "PlayPalmaActivity"}, + {505, &Hid::SetPalmaFrModeType, "SetPalmaFrModeType"}, + {506, &Hid::ReadPalmaStep, "ReadPalmaStep"}, + {507, &Hid::EnablePalmaStep, "EnablePalmaStep"}, + {508, &Hid::ResetPalmaStep, "ResetPalmaStep"}, + {509, &Hid::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"}, + {510, &Hid::WritePalmaApplicationSection, "WritePalmaApplicationSection"}, + {511, &Hid::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"}, + {512, &Hid::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"}, + {513, &Hid::WritePalmaActivityEntry, "WritePalmaActivityEntry"}, + {514, &Hid::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"}, + {515, &Hid::WritePalmaWaveEntry, "WritePalmaWaveEntry"}, + {516, &Hid::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"}, + {517, &Hid::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"}, + {518, &Hid::SuspendPalmaFeature, "SuspendPalmaFeature"}, + {519, &Hid::GetPalmaOperationResult, "GetPalmaOperationResult"}, + {520, &Hid::ReadPalmaPlayLog, "ReadPalmaPlayLog"}, + {521, &Hid::ResetPalmaPlayLog, "ResetPalmaPlayLog"}, {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"}, - {523, nullptr, "SetIsPalmaPairedConnectable"}, - {524, nullptr, "PairPalma"}, + {523, &Hid::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"}, + {524, &Hid::PairPalma, "PairPalma"}, {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, - {526, nullptr, "CancelWritePalmaWaveEntry"}, - {527, nullptr, "EnablePalmaBoostMode"}, - {528, nullptr, "GetPalmaBluetoothAddress"}, - {529, nullptr, "SetDisallowedPalmaConnection"}, + {526, &Hid::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"}, + {527, &Hid::EnablePalmaBoostMode, "EnablePalmaBoostMode"}, + {528, &Hid::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"}, + {529, &Hid::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"}, {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"}, {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, {1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"}, - {1003, nullptr, "IsFirmwareUpdateNeededForNotification"}, + {1003, &Hid::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"}, {2000, nullptr, "ActivateDigitizer"}, }; // clang-format on @@ -527,8 +511,8 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetSixAxisEnabled(parameters.sixaxis_handle, true); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, true); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -536,7 +520,7 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { @@ -550,8 +534,8 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetSixAxisEnabled(parameters.sixaxis_handle, false); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, false); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -559,7 +543,33 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); +} + +void Hid::IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + bool is_enabled{}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = + controller.IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(is_enabled); } void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { @@ -574,9 +584,9 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetSixAxisFusionEnabled(parameters.sixaxis_handle, - parameters.enable_sixaxis_sensor_fusion); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, + parameters.enable_sixaxis_sensor_fusion); LOG_DEBUG(Service_HID, "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " @@ -586,7 +596,7 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { @@ -601,8 +611,9 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = + controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " @@ -612,7 +623,7 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { @@ -626,9 +637,11 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - const auto sixaxis_fusion_parameters = - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetSixAxisFusionParameters(parameters.sixaxis_handle); + Core::HID::SixAxisSensorFusionParameters fusion_parameters{}; + const auto& controller = + GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = + controller.GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -636,8 +649,8 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushRaw(sixaxis_fusion_parameters); + rb.Push(result); + rb.PushRaw(fusion_parameters); } void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { @@ -651,8 +664,15 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .ResetSixAxisFusionParameters(parameters.sixaxis_handle); + // Since these parameters are unknow just use what HW outputs + const Core::HID::SixAxisSensorFusionParameters fusion_parameters{ + .parameter1 = 0.03f, + .parameter2 = 0.4f, + }; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result1 = + controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); + const auto result2 = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, true); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -660,7 +680,11 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + if (result1.IsError()) { + rb.Push(result1); + return; + } + rb.Push(result2); } void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { @@ -669,8 +693,8 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " @@ -679,7 +703,7 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { drift_mode, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { @@ -693,15 +717,18 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; + auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); + LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetGyroscopeZeroDriftMode(parameters.sixaxis_handle)); + rb.Push(result); + rb.PushEnum(drift_mode); } void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { @@ -714,10 +741,10 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); const auto parameters{rp.PopRaw<Parameters>()}; - const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); + const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -725,7 +752,7 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { @@ -739,6 +766,10 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; + bool is_at_rest{}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); + LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, @@ -746,8 +777,7 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .IsSixAxisSensorAtRest(parameters.sixaxis_handle)); + rb.Push(is_at_rest); } void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) { @@ -761,6 +791,11 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c const auto parameters{rp.PopRaw<Parameters>()}; + bool is_firmware_available{}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + controller.IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle, + is_firmware_available); + LOG_WARNING( Service_HID, "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -769,7 +804,145 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(false); + rb.Push(is_firmware_available); +} + +void Hid::EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool enabled; + Core::HID::SixAxisSensorHandle sixaxis_handle; + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.EnableSixAxisSensorUnalteredPassthrough( + parameters.sixaxis_handle, parameters.enabled); + + LOG_DEBUG(Service_HID, + "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, " + "applet_resource_user_id={}", + parameters.enabled, parameters.sixaxis_handle.npad_type, + parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, + parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + bool is_unaltered_sisxaxis_enabled{}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled( + parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled); + + LOG_DEBUG( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(is_unaltered_sisxaxis_enabled); +} + +void Hid::LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + Core::HID::SixAxisSensorCalibrationParameter calibration{}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = + controller.LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration); + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + if (result.IsSuccess()) { + ctx.WriteBuffer(calibration); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + Core::HID::SixAxisSensorIcInformation ic_information{}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = + controller.GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information); + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + if (result.IsSuccess()) { + ctx.WriteBuffer(ic_information); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = + controller.ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle); + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); } void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { @@ -878,6 +1051,10 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); + // Games expect this event to be signaled after calling this function + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SignalStyleSetChangedEvent(parameters.npad_id); + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) @@ -895,8 +1072,8 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .DisconnectNpad(parameters.npad_id); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + controller.DisconnectNpad(parameters.npad_id); LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, parameters.applet_resource_user_id); @@ -909,13 +1086,15 @@ void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; + Core::HID::LedPattern pattern{0, 0, 0, 0}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.GetLedPattern(npad_id, pattern); + LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetLedPattern(npad_id) - .raw); + rb.Push(result); + rb.Push(pattern.raw); } void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { @@ -975,9 +1154,9 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left, - Controller_NPad::NpadJoyAssignmentMode::Single); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left, + Controller_NPad::NpadJoyAssignmentMode::Single); LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, parameters.applet_resource_user_id); @@ -998,9 +1177,9 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type, - Controller_NPad::NpadJoyAssignmentMode::Single); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type, + Controller_NPad::NpadJoyAssignmentMode::Single); LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", parameters.npad_id, parameters.applet_resource_user_id, @@ -1021,8 +1200,8 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + controller.SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual); LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, parameters.applet_resource_user_id); @@ -1037,14 +1216,14 @@ void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", npad_id_1, npad_id_2, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) { @@ -1104,19 +1283,14 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SwapNpadAssignment(npad_id_1, npad_id_2); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SwapNpadAssignment(npad_id_1, npad_id_2); LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", npad_id_1, npad_id_2, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - if (res) { - rb.Push(ResultSuccess); - } else { - LOG_ERROR(Service_HID, "Npads are not connected!"); - rb.Push(ERR_NPAD_NOT_CONNECTED); - } + rb.Push(result); } void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) { @@ -1130,13 +1304,17 @@ void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext const auto parameters{rp.PopRaw<Parameters>()}; + bool is_enabled = false; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = + controller.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled); + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id)); + rb.Push(result); + rb.Push(is_enabled); } void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) { @@ -1151,9 +1329,9 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetUnintendedHomeButtonInputProtectionEnabled( - parameters.unintended_home_button_input_protection, parameters.npad_id); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled( + parameters.unintended_home_button_input_protection, parameters.npad_id); LOG_WARNING(Service_HID, "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={}," @@ -1162,7 +1340,7 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) { @@ -1222,8 +1400,11 @@ void Hid::ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) { void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; + const auto& controller = + GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); Core::HID::VibrationDeviceInfo vibration_device_info; + bool check_device_index = false; switch (vibration_device_handle.npad_type) { case Core::HID::NpadStyleIndex::ProController: @@ -1231,34 +1412,46 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::JoyconLeft: case Core::HID::NpadStyleIndex::JoyconRight: - default: vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator; + check_device_index = true; break; case Core::HID::NpadStyleIndex::GameCube: vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm; break; - case Core::HID::NpadStyleIndex::Pokeball: + case Core::HID::NpadStyleIndex::N64: + vibration_device_info.type = Core::HID::VibrationDeviceType::N64; + break; + default: vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown; break; } - switch (vibration_device_handle.device_index) { - case Core::HID::DeviceIndex::Left: - vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; - break; - case Core::HID::DeviceIndex::Right: - vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; - break; - case Core::HID::DeviceIndex::None: - default: - UNREACHABLE_MSG("DeviceIndex should never be None!"); - vibration_device_info.position = Core::HID::VibrationDevicePosition::None; - break; + vibration_device_info.position = Core::HID::VibrationDevicePosition::None; + if (check_device_index) { + switch (vibration_device_handle.device_index) { + case Core::HID::DeviceIndex::Left: + vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; + break; + case Core::HID::DeviceIndex::Right: + vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; + break; + case Core::HID::DeviceIndex::None: + default: + ASSERT_MSG(false, "DeviceIndex should never be None!"); + break; + } } LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", vibration_device_info.type, vibration_device_info.position); + const auto result = controller.IsDeviceHandleValid(vibration_device_handle); + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); rb.PushRaw(vibration_device_info); @@ -1324,6 +1517,8 @@ void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto can_vibrate{rp.Pop<bool>()}; + // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value + // by converting it to a bool Settings::values.vibration_enabled.SetValue(can_vibrate); LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); @@ -1335,9 +1530,12 @@ void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called"); + // nnSDK checks if a float is greater than zero. We return the bool we stored earlier + const auto is_enabled = Settings::values.vibration_enabled.GetValue(); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(Settings::values.vibration_enabled.GetValue()); + rb.Push(is_enabled); } void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { @@ -1683,14 +1881,361 @@ void Hid::IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx) { rb.Push(false); } +void Hid::GetPalmaConnectionHandle(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", + parameters.npad_id, parameters.applet_resource_user_id); + + Controller_Palma::PalmaConnectionHandle handle; + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + const auto result = controller.GetPalmaConnectionHandle(parameters.npad_id, handle); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.PushRaw(handle); +} + +void Hid::InitializePalma(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + const auto result = controller.InitializePalma(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::AcquirePalmaOperationCompleteEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(controller.AcquirePalmaOperationCompleteEvent(connection_handle)); +} + +void Hid::GetPalmaOperationInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + Controller_Palma::PalmaOperationType operation_type; + Controller_Palma::PalmaOperationData data; + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + const auto result = controller.GetPalmaOperationInfo(connection_handle, operation_type, data); + + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + ctx.WriteBuffer(data); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push(static_cast<u64>(operation_type)); +} + +void Hid::PlayPalmaActivity(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + const auto palma_activity{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}", + connection_handle.npad_id, palma_activity); + + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + const auto result = controller.PlayPalmaActivity(connection_handle, palma_activity); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::SetPalmaFrModeType(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + const auto fr_mode{rp.PopEnum<Controller_Palma::PalmaFrModeType>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}", + connection_handle.npad_id, fr_mode); + + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + const auto result = controller.SetPalmaFrModeType(connection_handle, fr_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ReadPalmaStep(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + const auto result = controller.ReadPalmaStep(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::EnablePalmaStep(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool is_enabled; + INSERT_PADDING_WORDS_NOINIT(1); + Controller_Palma::PalmaConnectionHandle connection_handle; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}", + parameters.connection_handle.npad_id, parameters.is_enabled); + + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + const auto result = + controller.EnablePalmaStep(parameters.connection_handle, parameters.is_enabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ResetPalmaStep(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + const auto result = controller.ResetPalmaStep(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ReadPalmaApplicationSection(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::WritePalmaApplicationSection(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::ReadPalmaUniqueCode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .ReadPalmaUniqueCode(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetPalmaUniqueCodeInvalid(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .SetPalmaUniqueCodeInvalid(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::WritePalmaActivityEntry(Kernel::HLERequestContext& ctx) { + LOG_CRITICAL(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + const auto unknown{rp.Pop<u64>()}; + + const auto buffer = ctx.ReadBuffer(); + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}", + connection_handle.npad_id, unknown); + + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .WritePalmaRgbLedPatternEntry(connection_handle, unknown); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::WritePalmaWaveEntry(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + const auto wave_set{rp.PopEnum<Controller_Palma::PalmaWaveSet>()}; + const auto unknown{rp.Pop<u64>()}; + const auto t_mem_size{rp.Pop<u64>()}; + const auto t_mem_handle{ctx.GetCopyHandle(0)}; + const auto size{rp.Pop<u64>()}; + + ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes"); + + auto t_mem = + system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); + + if (t_mem.IsNull()) { + LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size"); + + LOG_WARNING(Service_HID, + "(STUBBED) called, connection_handle={}, wave_set={}, unkown={}, " + "t_mem_handle=0x{:08X}, t_mem_size={}, size={}", + connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size); + + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .WritePalmaWaveEntry(connection_handle, wave_set, + system.Memory().GetPointer(t_mem->GetSourceAddress()), t_mem_size); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + s32 database_id_version; + INSERT_PADDING_WORDS_NOINIT(1); + Controller_Palma::PalmaConnectionHandle connection_handle; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}", + parameters.connection_handle.npad_id, parameters.database_id_version); + + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .SetPalmaDataBaseIdentificationVersion(parameters.connection_handle, + parameters.database_id_version); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::GetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .GetPalmaDataBaseIdentificationVersion(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SuspendPalmaFeature(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::GetPalmaOperationResult(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + const auto result = applet_resource->GetController<Controller_Palma>(HidController::Palma) + .GetPalmaOperationResult(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ReadPalmaPlayLog(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::ResetPalmaPlayLog(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - const auto is_palma_all_connectable{rp.Pop<bool>()}; + struct Parameters { + bool is_palma_all_connectable; + INSERT_PADDING_BYTES_NOINIT(7); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; LOG_WARNING(Service_HID, - "(STUBBED) called, applet_resource_user_id={}, is_palma_all_connectable={}", - applet_resource_user_id, is_palma_all_connectable); + "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}", + parameters.is_palma_all_connectable, parameters.applet_resource_user_id); + + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .SetIsPalmaAllConnectable(parameters.is_palma_all_connectable); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetIsPalmaPairedConnectable(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::PairPalma(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .PairPalma(connection_handle); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -1702,6 +2247,37 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .SetPalmaBoostMode(palma_boost_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::CancelWritePalmaWaveEntry(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::EnablePalmaBoostMode(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::GetPalmaBluetoothAddress(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetDisallowedPalmaConnection(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } @@ -1744,6 +2320,25 @@ void Hid::SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } +void Hid::IsFirmwareUpdateNeededForNotification(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + s32 unknown; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}", + parameters.unknown, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(false); +} + class HidDbg final : public ServiceFramework<HidDbg> { public: explicit HidDbg(Core::System& system_) : ServiceFramework{system_, "hid:dbg"} { @@ -1932,12 +2527,18 @@ public: {324, nullptr, "GetUniquePadButtonSet"}, {325, nullptr, "GetUniquePadColor"}, {326, nullptr, "GetUniquePadAppletDetailedUiType"}, + {327, nullptr, "GetAbstractedPadIdDataFromNpad"}, + {328, nullptr, "AttachAbstractedPadToNpad"}, + {329, nullptr, "DetachAbstractedPadAll"}, + {330, nullptr, "CheckAbstractedPadConnection"}, {500, nullptr, "SetAppletResourceUserId"}, {501, nullptr, "RegisterAppletResourceUserId"}, {502, nullptr, "UnregisterAppletResourceUserId"}, {503, nullptr, "EnableAppletToGetInput"}, {504, nullptr, "SetAruidValidForVibration"}, {505, nullptr, "EnableAppletToGetSixAxisSensor"}, + {506, nullptr, "EnableAppletToGetPadInput"}, + {507, nullptr, "EnableAppletToGetTouchScreen"}, {510, nullptr, "SetVibrationMasterVolume"}, {511, nullptr, "GetVibrationMasterVolume"}, {512, nullptr, "BeginPermitVibrationSession"}, @@ -2124,32 +2725,6 @@ public: } }; -class HidBus final : public ServiceFramework<HidBus> { -public: - explicit HidBus(Core::System& system_) : ServiceFramework{system_, "hidbus"} { - // clang-format off - static const FunctionInfo functions[] = { - {1, nullptr, "GetBusHandle"}, - {2, nullptr, "IsExternalDeviceConnected"}, - {3, nullptr, "Initialize"}, - {4, nullptr, "Finalize"}, - {5, nullptr, "EnableExternalDevice"}, - {6, nullptr, "GetExternalDeviceId"}, - {7, nullptr, "SendCommandAsync"}, - {8, nullptr, "GetSendCommandAsynceResult"}, - {9, nullptr, "SetEventForSendCommandAsycResult"}, - {10, nullptr, "GetSharedMemoryHandle"}, - {11, nullptr, "EnableJoyPollingReceiveMode"}, - {12, nullptr, "DisableJoyPollingReceiveMode"}, - {13, nullptr, "GetPollingData"}, - {14, nullptr, "SetStatusManagerType"}, - }; - // clang-format on - - RegisterHandlers(functions); - } -}; - void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { std::make_shared<Hid>(system)->InstallAsService(service_manager); std::make_shared<HidBus>(system)->InstallAsService(service_manager); @@ -2157,8 +2732,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system std::make_shared<HidSys>(system)->InstallAsService(service_manager); std::make_shared<HidTmp>(system)->InstallAsService(service_manager); - std::make_shared<IRS>(system)->InstallAsService(service_manager); - std::make_shared<IRS_SYS>(system)->InstallAsService(service_manager); + std::make_shared<Service::IRS::IRS>(system)->InstallAsService(service_manager); + std::make_shared<Service::IRS::IRS_SYS>(system)->InstallAsService(service_manager); std::make_shared<XCD_SYS>(system)->InstallAsService(service_manager); } diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index c281081a7..340d26fdc 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -34,6 +33,7 @@ enum class HidController : std::size_t { NPad, Gesture, ConsoleSixAxisSensor, + Palma, MaxControllers, }; @@ -59,13 +59,14 @@ public: private: template <typename T> - void MakeController(HidController controller) { - controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system.HIDCore()); + void MakeController(HidController controller, u8* shared_memory) { + controllers[static_cast<std::size_t>(controller)] = + std::make_unique<T>(system.HIDCore(), shared_memory); } template <typename T> - void MakeControllerWithServiceContext(HidController controller) { + void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) { controllers[static_cast<std::size_t>(controller)] = - std::make_unique<T>(system.HIDCore(), service_context); + std::make_unique<T>(system.HIDCore(), shared_memory, service_context); } void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); @@ -103,6 +104,7 @@ private: void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx); void StartSixAxisSensor(Kernel::HLERequestContext& ctx); void StopSixAxisSensor(Kernel::HLERequestContext& ctx); + void IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx); void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx); void SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx); void GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx); @@ -112,6 +114,11 @@ private: void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx); void IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx); + void EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx); + void IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx); + void LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx); + void GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx); + void ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx); void ActivateGesture(Kernel::HLERequestContext& ctx); void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); @@ -160,11 +167,40 @@ private: void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx); void IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx); + void GetPalmaConnectionHandle(Kernel::HLERequestContext& ctx); + void InitializePalma(Kernel::HLERequestContext& ctx); + void AcquirePalmaOperationCompleteEvent(Kernel::HLERequestContext& ctx); + void GetPalmaOperationInfo(Kernel::HLERequestContext& ctx); + void PlayPalmaActivity(Kernel::HLERequestContext& ctx); + void SetPalmaFrModeType(Kernel::HLERequestContext& ctx); + void ReadPalmaStep(Kernel::HLERequestContext& ctx); + void EnablePalmaStep(Kernel::HLERequestContext& ctx); + void ResetPalmaStep(Kernel::HLERequestContext& ctx); + void ReadPalmaApplicationSection(Kernel::HLERequestContext& ctx); + void WritePalmaApplicationSection(Kernel::HLERequestContext& ctx); + void ReadPalmaUniqueCode(Kernel::HLERequestContext& ctx); + void SetPalmaUniqueCodeInvalid(Kernel::HLERequestContext& ctx); + void WritePalmaActivityEntry(Kernel::HLERequestContext& ctx); + void WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx); + void WritePalmaWaveEntry(Kernel::HLERequestContext& ctx); + void SetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx); + void GetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx); + void SuspendPalmaFeature(Kernel::HLERequestContext& ctx); + void GetPalmaOperationResult(Kernel::HLERequestContext& ctx); + void ReadPalmaPlayLog(Kernel::HLERequestContext& ctx); + void ResetPalmaPlayLog(Kernel::HLERequestContext& ctx); void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); + void SetIsPalmaPairedConnectable(Kernel::HLERequestContext& ctx); + void PairPalma(Kernel::HLERequestContext& ctx); void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); + void CancelWritePalmaWaveEntry(Kernel::HLERequestContext& ctx); + void EnablePalmaBoostMode(Kernel::HLERequestContext& ctx); + void GetPalmaBluetoothAddress(Kernel::HLERequestContext& ctx); + void SetDisallowedPalmaConnection(Kernel::HLERequestContext& ctx); void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx); void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx); void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx); + void IsFirmwareUpdateNeededForNotification(Kernel::HLERequestContext& ctx); std::shared_ptr<IAppletResource> applet_resource; diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp new file mode 100644 index 000000000..e5e50845f --- /dev/null +++ b/src/core/hle/service/hid/hidbus.cpp @@ -0,0 +1,524 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/core_timing_util.h" +#include "core/hid/hid_types.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/service/hid/hidbus.h" +#include "core/hle/service/hid/hidbus/ringcon.h" +#include "core/hle/service/hid/hidbus/starlink.h" +#include "core/hle/service/hid/hidbus/stubbed.h" +#include "core/hle/service/service.h" +#include "core/memory.h" + +namespace Service::HID { +// (15ms, 66Hz) +constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; + +HidBus::HidBus(Core::System& system_) + : ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} { + + // clang-format off + static const FunctionInfo functions[] = { + {1, &HidBus::GetBusHandle, "GetBusHandle"}, + {2, &HidBus::IsExternalDeviceConnected, "IsExternalDeviceConnected"}, + {3, &HidBus::Initialize, "Initialize"}, + {4, &HidBus::Finalize, "Finalize"}, + {5, &HidBus::EnableExternalDevice, "EnableExternalDevice"}, + {6, &HidBus::GetExternalDeviceId, "GetExternalDeviceId"}, + {7, &HidBus::SendCommandAsync, "SendCommandAsync"}, + {8, &HidBus::GetSendCommandAsynceResult, "GetSendCommandAsynceResult"}, + {9, &HidBus::SetEventForSendCommandAsycResult, "SetEventForSendCommandAsycResult"}, + {10, &HidBus::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, + {11, &HidBus::EnableJoyPollingReceiveMode, "EnableJoyPollingReceiveMode"}, + {12, &HidBus::DisableJoyPollingReceiveMode, "DisableJoyPollingReceiveMode"}, + {13, nullptr, "GetPollingData"}, + {14, &HidBus::SetStatusManagerType, "SetStatusManagerType"}, + }; + // clang-format on + + RegisterHandlers(functions); + + // Register update callbacks + hidbus_update_event = Core::Timing::CreateEvent( + "Hidbus::UpdateCallback", + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + const auto guard = LockService(); + UpdateHidbus(user_data, ns_late); + return std::nullopt; + }); + + system_.CoreTiming().ScheduleLoopingEvent(hidbus_update_ns, hidbus_update_ns, + hidbus_update_event); +} + +HidBus::~HidBus() { + system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0); +} + +void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + if (is_hidbus_enabled) { + for (std::size_t i = 0; i < devices.size(); ++i) { + if (!devices[i].is_device_initializated) { + continue; + } + auto& device = devices[i].device; + device->OnUpdate(); + auto& cur_entry = hidbus_status.entries[devices[i].handle.internal_index]; + cur_entry.is_polling_mode = device->IsPollingMode(); + cur_entry.polling_mode = device->GetPollingMode(); + cur_entry.is_enabled = device->IsEnabled(); + + u8* shared_memory = system.Kernel().GetHidBusSharedMem().GetPointer(); + std::memcpy(shared_memory + (i * sizeof(HidbusStatusManagerEntry)), &hidbus_status, + sizeof(HidbusStatusManagerEntry)); + } + } +} + +std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const { + for (std::size_t i = 0; i < devices.size(); ++i) { + const auto& device_handle = devices[i].handle; + if (handle.abstracted_pad_id == device_handle.abstracted_pad_id && + handle.internal_index == device_handle.internal_index && + handle.player_number == device_handle.player_number && + handle.bus_type == device_handle.bus_type && + handle.is_valid == device_handle.is_valid) { + return i; + } + } + return std::nullopt; +} + +void HidBus::GetBusHandle(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + BusType bus_type; + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}", + parameters.npad_id, parameters.bus_type, parameters.applet_resource_user_id); + + bool is_handle_found = 0; + std::size_t handle_index = 0; + + for (std::size_t i = 0; i < devices.size(); i++) { + const auto& handle = devices[i].handle; + if (!handle.is_valid) { + continue; + } + if (static_cast<Core::HID::NpadIdType>(handle.player_number) == parameters.npad_id && + handle.bus_type == parameters.bus_type) { + is_handle_found = true; + handle_index = i; + break; + } + } + + // Handle not found. Create a new one + if (!is_handle_found) { + for (std::size_t i = 0; i < devices.size(); i++) { + if (devices[i].handle.is_valid) { + continue; + } + devices[i].handle = { + .abstracted_pad_id = static_cast<u8>(i), + .internal_index = static_cast<u8>(i), + .player_number = static_cast<u8>(parameters.npad_id), + .bus_type = parameters.bus_type, + .is_valid = true, + }; + handle_index = i; + break; + } + } + + struct OutData { + bool is_valid; + INSERT_PADDING_BYTES(7); + BusHandle handle; + }; + static_assert(sizeof(OutData) == 0x10, "OutData has incorrect size."); + + const OutData out_data{ + .is_valid = true, + .handle = devices[handle_index].handle, + }; + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(out_data); +} + +void HidBus::IsExternalDeviceConnected(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_INFO(Service_HID, + "Called, abstracted_pad_id={}, bus_type={}, internal_index={}, " + "player_number={}, is_valid={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto& device = devices[device_index.value()].device; + const bool is_attached = device->IsDeviceActivated(); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_attached); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::Initialize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_INFO(Service_HID, + "called, abstracted_pad_id={} bus_type={} internal_index={} " + "player_number={} is_valid={}, applet_resource_user_id={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id); + + is_hidbus_enabled = true; + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto entry_index = devices[device_index.value()].handle.internal_index; + auto& cur_entry = hidbus_status.entries[entry_index]; + + if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) { + MakeDevice<RingController>(bus_handle_); + devices[device_index.value()].is_device_initializated = true; + devices[device_index.value()].device->ActivateDevice(); + cur_entry.is_in_focus = true; + cur_entry.is_connected = true; + cur_entry.is_connected_result = ResultSuccess; + cur_entry.is_enabled = false; + cur_entry.is_polling_mode = false; + } else { + MakeDevice<HidbusStubbed>(bus_handle_); + devices[device_index.value()].is_device_initializated = true; + cur_entry.is_in_focus = true; + cur_entry.is_connected = false; + cur_entry.is_connected_result = ResultSuccess; + cur_entry.is_enabled = false; + cur_entry.is_polling_mode = false; + } + + std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status, + sizeof(hidbus_status)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::Finalize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_INFO(Service_HID, + "called, abstracted_pad_id={}, bus_type={}, internal_index={}, " + "player_number={}, is_valid={}, applet_resource_user_id={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto entry_index = devices[device_index.value()].handle.internal_index; + auto& cur_entry = hidbus_status.entries[entry_index]; + auto& device = devices[device_index.value()].device; + devices[device_index.value()].is_device_initializated = false; + device->DeactivateDevice(); + + cur_entry.is_in_focus = true; + cur_entry.is_connected = false; + cur_entry.is_connected_result = ResultSuccess; + cur_entry.is_enabled = false; + cur_entry.is_polling_mode = false; + std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status, + sizeof(hidbus_status)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::EnableExternalDevice(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool enable; + INSERT_PADDING_BYTES_NOINIT(7); + BusHandle bus_handle; + u64 inval; + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_INFO(Service_HID, + "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " + "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}", + parameters.enable, parameters.bus_handle.abstracted_pad_id, + parameters.bus_handle.bus_type, parameters.bus_handle.internal_index, + parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval, + parameters.applet_resource_user_id); + + const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle); + + if (device_index) { + auto& device = devices[device_index.value()].device; + device->Enable(parameters.enable); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::GetExternalDeviceId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_INFO(Service_HID, + "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " + "is_valid={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto& device = devices[device_index.value()].device; + u32 device_id = device->GetDeviceId(); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(device_id); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::SendCommandAsync(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto data = ctx.ReadBuffer(); + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_DEBUG(Service_HID, + "called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " + "player_number={}, is_valid={}", + data.size(), bus_handle_.abstracted_pad_id, bus_handle_.bus_type, + bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + auto& device = devices[device_index.value()].device; + device->SetCommand(data); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +}; + +void HidBus::GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_DEBUG(Service_HID, + "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " + "is_valid={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto& device = devices[device_index.value()].device; + const std::vector<u8> data = device->GetReply(); + const u64 data_size = ctx.WriteBuffer(data); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(data_size); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +}; + +void HidBus::SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_INFO(Service_HID, + "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " + "is_valid={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto& device = devices[device_index.value()].device; + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device->GetSendCommandAsycEvent()); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +}; + +void HidBus::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(&system.Kernel().GetHidBusSharedMem()); +} + +void HidBus::EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto t_mem_size{rp.Pop<u32>()}; + const auto t_mem_handle{ctx.GetCopyHandle(0)}; + const auto polling_mode_{rp.PopEnum<JoyPollingMode>()}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes"); + + auto t_mem = + system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); + + if (t_mem.IsNull()) { + LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + ASSERT_MSG(t_mem->GetSize() == 0x1000, "t_mem has incorrect size"); + + LOG_INFO(Service_HID, + "called, t_mem_handle=0x{:08X}, polling_mode={}, abstracted_pad_id={}, bus_type={}, " + "internal_index={}, player_number={}, is_valid={}", + t_mem_handle, polling_mode_, bus_handle_.abstracted_pad_id, bus_handle_.bus_type, + bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + auto& device = devices[device_index.value()].device; + device->SetPollingMode(polling_mode_); + device->SetTransferMemoryPointer(system.Memory().GetPointer(t_mem->GetSourceAddress())); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_INFO(Service_HID, + "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " + "is_valid={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + auto& device = devices[device_index.value()].device; + device->DisablePollingMode(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::SetStatusManagerType(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto manager_type{rp.PopEnum<StatusManagerType>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h new file mode 100644 index 000000000..8c687f678 --- /dev/null +++ b/src/core/hle/service/hid/hidbus.h @@ -0,0 +1,130 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <functional> + +#include "core/hle/service/hid/hidbus/hidbus_base.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Core::Timing { +struct EventType; +} // namespace Core::Timing + +namespace Core { +class System; +} // namespace Core + +namespace Service::HID { + +class HidBus final : public ServiceFramework<HidBus> { +public: + explicit HidBus(Core::System& system_); + ~HidBus() override; + +private: + static const std::size_t max_number_of_handles = 0x13; + + enum class HidBusDeviceId : std::size_t { + RingController = 0x20, + FamicomRight = 0x21, + Starlink = 0x28, + }; + + // This is nn::hidbus::detail::StatusManagerType + enum class StatusManagerType : u32 { + None, + Type16, + Type32, + }; + + // This is nn::hidbus::BusType + enum class BusType : u8 { + LeftJoyRail, + RightJoyRail, + InternalBus, // Lark microphone + + MaxBusType, + }; + + // This is nn::hidbus::BusHandle + struct BusHandle { + u32 abstracted_pad_id; + u8 internal_index; + u8 player_number; + BusType bus_type; + bool is_valid; + }; + static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size"); + + // This is nn::hidbus::JoyPollingReceivedData + struct JoyPollingReceivedData { + std::array<u8, 0x30> data; + u64 out_size; + u64 sampling_number; + }; + static_assert(sizeof(JoyPollingReceivedData) == 0x40, + "JoyPollingReceivedData is an invalid size"); + + struct HidbusStatusManagerEntry { + u8 is_connected{}; + INSERT_PADDING_BYTES(0x3); + Result is_connected_result{0}; + u8 is_enabled{}; + u8 is_in_focus{}; + u8 is_polling_mode{}; + u8 reserved{}; + JoyPollingMode polling_mode{}; + INSERT_PADDING_BYTES(0x70); // Unknown + }; + static_assert(sizeof(HidbusStatusManagerEntry) == 0x80, + "HidbusStatusManagerEntry is an invalid size"); + + struct HidbusStatusManager { + std::array<HidbusStatusManagerEntry, max_number_of_handles> entries{}; + INSERT_PADDING_BYTES(0x680); // Unused + }; + static_assert(sizeof(HidbusStatusManager) <= 0x1000, "HidbusStatusManager is an invalid size"); + + struct HidbusDevice { + bool is_device_initializated{}; + BusHandle handle{}; + std::unique_ptr<HidbusBase> device{nullptr}; + }; + + void GetBusHandle(Kernel::HLERequestContext& ctx); + void IsExternalDeviceConnected(Kernel::HLERequestContext& ctx); + void Initialize(Kernel::HLERequestContext& ctx); + void Finalize(Kernel::HLERequestContext& ctx); + void EnableExternalDevice(Kernel::HLERequestContext& ctx); + void GetExternalDeviceId(Kernel::HLERequestContext& ctx); + void SendCommandAsync(Kernel::HLERequestContext& ctx); + void GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx); + void SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx); + void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); + void EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx); + void DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx); + void SetStatusManagerType(Kernel::HLERequestContext& ctx); + + void UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const; + + template <typename T> + void MakeDevice(BusHandle handle) { + const auto device_index = GetDeviceIndexFromHandle(handle); + if (device_index) { + devices[device_index.value()].device = + std::make_unique<T>(system.HIDCore(), service_context); + } + } + + bool is_hidbus_enabled{false}; + HidbusStatusManager hidbus_status{}; + std::array<HidbusDevice, max_number_of_handles> devices{}; + std::shared_ptr<Core::Timing::EventType> hidbus_update_event; + KernelHelpers::ServiceContext service_context; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.cpp b/src/core/hle/service/hid/hidbus/hidbus_base.cpp new file mode 100644 index 000000000..b569b3c20 --- /dev/null +++ b/src/core/hle/service/hid/hidbus/hidbus_base.cpp @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hid/hid_core.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/service/hid/hidbus/hidbus_base.h" +#include "core/hle/service/kernel_helpers.h" + +namespace Service::HID { + +HidbusBase::HidbusBase(KernelHelpers::ServiceContext& service_context_) + : service_context(service_context_) { + send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent"); +} +HidbusBase::~HidbusBase() = default; + +void HidbusBase::ActivateDevice() { + if (is_activated) { + return; + } + is_activated = true; + OnInit(); +} + +void HidbusBase::DeactivateDevice() { + if (is_activated) { + OnRelease(); + } + is_activated = false; +} + +bool HidbusBase::IsDeviceActivated() const { + return is_activated; +} + +void HidbusBase::Enable(bool enable) { + device_enabled = enable; +} + +bool HidbusBase::IsEnabled() const { + return device_enabled; +} + +bool HidbusBase::IsPollingMode() const { + return polling_mode_enabled; +} + +JoyPollingMode HidbusBase::GetPollingMode() const { + return polling_mode; +} + +void HidbusBase::SetPollingMode(JoyPollingMode mode) { + polling_mode = mode; + polling_mode_enabled = true; +} + +void HidbusBase::DisablePollingMode() { + polling_mode_enabled = false; +} + +void HidbusBase::SetTransferMemoryPointer(u8* t_mem) { + is_transfer_memory_set = true; + transfer_memory = t_mem; +} + +Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const { + return send_command_async_event->GetReadableEvent(); +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.h b/src/core/hle/service/hid/hidbus/hidbus_base.h new file mode 100644 index 000000000..d3960f506 --- /dev/null +++ b/src/core/hle/service/hid/hidbus/hidbus_base.h @@ -0,0 +1,178 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include "common/common_types.h" +#include "core/hle/result.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Service::KernelHelpers { +class ServiceContext; +} + +namespace Service::HID { + +// This is nn::hidbus::JoyPollingMode +enum class JoyPollingMode : u32 { + SixAxisSensorDisable, + SixAxisSensorEnable, + ButtonOnly, +}; + +struct DataAccessorHeader { + Result result{ResultUnknown}; + INSERT_PADDING_WORDS(0x1); + std::array<u8, 0x18> unused{}; + u64 latest_entry{}; + u64 total_entries{}; +}; +static_assert(sizeof(DataAccessorHeader) == 0x30, "DataAccessorHeader is an invalid size"); + +struct JoyDisableSixAxisPollingData { + std::array<u8, 0x26> data; + u8 out_size; + INSERT_PADDING_BYTES(0x1); + u64 sampling_number; +}; +static_assert(sizeof(JoyDisableSixAxisPollingData) == 0x30, + "JoyDisableSixAxisPollingData is an invalid size"); + +struct JoyEnableSixAxisPollingData { + std::array<u8, 0x8> data; + u8 out_size; + INSERT_PADDING_BYTES(0x7); + u64 sampling_number; +}; +static_assert(sizeof(JoyEnableSixAxisPollingData) == 0x18, + "JoyEnableSixAxisPollingData is an invalid size"); + +struct JoyButtonOnlyPollingData { + std::array<u8, 0x2c> data; + u8 out_size; + INSERT_PADDING_BYTES(0x3); + u64 sampling_number; +}; +static_assert(sizeof(JoyButtonOnlyPollingData) == 0x38, + "JoyButtonOnlyPollingData is an invalid size"); + +struct JoyDisableSixAxisPollingEntry { + u64 sampling_number; + JoyDisableSixAxisPollingData polling_data; +}; +static_assert(sizeof(JoyDisableSixAxisPollingEntry) == 0x38, + "JoyDisableSixAxisPollingEntry is an invalid size"); + +struct JoyEnableSixAxisPollingEntry { + u64 sampling_number; + JoyEnableSixAxisPollingData polling_data; +}; +static_assert(sizeof(JoyEnableSixAxisPollingEntry) == 0x20, + "JoyEnableSixAxisPollingEntry is an invalid size"); + +struct JoyButtonOnlyPollingEntry { + u64 sampling_number; + JoyButtonOnlyPollingData polling_data; +}; +static_assert(sizeof(JoyButtonOnlyPollingEntry) == 0x40, + "JoyButtonOnlyPollingEntry is an invalid size"); + +struct JoyDisableSixAxisDataAccessor { + DataAccessorHeader header{}; + std::array<JoyDisableSixAxisPollingEntry, 0xb> entries{}; +}; +static_assert(sizeof(JoyDisableSixAxisDataAccessor) == 0x298, + "JoyDisableSixAxisDataAccessor is an invalid size"); + +struct JoyEnableSixAxisDataAccessor { + DataAccessorHeader header{}; + std::array<JoyEnableSixAxisPollingEntry, 0xb> entries{}; +}; +static_assert(sizeof(JoyEnableSixAxisDataAccessor) == 0x190, + "JoyEnableSixAxisDataAccessor is an invalid size"); + +struct ButtonOnlyPollingDataAccessor { + DataAccessorHeader header; + std::array<JoyButtonOnlyPollingEntry, 0xb> entries; +}; +static_assert(sizeof(ButtonOnlyPollingDataAccessor) == 0x2F0, + "ButtonOnlyPollingDataAccessor is an invalid size"); + +class HidbusBase { +public: + explicit HidbusBase(KernelHelpers::ServiceContext& service_context_); + virtual ~HidbusBase(); + + void ActivateDevice(); + + void DeactivateDevice(); + + bool IsDeviceActivated() const; + + // Enables/disables the device + void Enable(bool enable); + + // returns true if device is enabled + bool IsEnabled() const; + + // returns true if polling mode is enabled + bool IsPollingMode() const; + + // returns polling mode + JoyPollingMode GetPollingMode() const; + + // Sets and enables JoyPollingMode + void SetPollingMode(JoyPollingMode mode); + + // Disables JoyPollingMode + void DisablePollingMode(); + + // Called on EnableJoyPollingReceiveMode + void SetTransferMemoryPointer(u8* t_mem); + + Kernel::KReadableEvent& GetSendCommandAsycEvent() const; + + virtual void OnInit() {} + + virtual void OnRelease() {} + + // Updates device transfer memory + virtual void OnUpdate() {} + + // Returns the device ID of the joycon + virtual u8 GetDeviceId() const { + return {}; + } + + // Assigns a command from data + virtual bool SetCommand(const std::vector<u8>& data) { + return {}; + } + + // Returns a reply from a command + virtual std::vector<u8> GetReply() const { + return {}; + } + +protected: + bool is_activated{}; + bool device_enabled{}; + bool polling_mode_enabled{}; + JoyPollingMode polling_mode = {}; + // TODO(German77): All data accessors need to be replaced with a ring lifo object + JoyDisableSixAxisDataAccessor disable_sixaxis_data{}; + JoyEnableSixAxisDataAccessor enable_sixaxis_data{}; + ButtonOnlyPollingDataAccessor button_only_data{}; + + u8* transfer_memory{nullptr}; + bool is_transfer_memory_set{}; + + Kernel::KEvent* send_command_async_event; + KernelHelpers::ServiceContext& service_context; +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/ringcon.cpp b/src/core/hle/service/hid/hidbus/ringcon.cpp new file mode 100644 index 000000000..ad223d649 --- /dev/null +++ b/src/core/hle/service/hid/hidbus/ringcon.cpp @@ -0,0 +1,285 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hid/emulated_devices.h" +#include "core/hid/hid_core.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/service/hid/hidbus/ringcon.h" + +namespace Service::HID { + +RingController::RingController(Core::HID::HIDCore& hid_core_, + KernelHelpers::ServiceContext& service_context_) + : HidbusBase(service_context_) { + input = hid_core_.GetEmulatedDevices(); +} + +RingController::~RingController() = default; + +void RingController::OnInit() { + return; +} + +void RingController::OnRelease() { + return; +}; + +void RingController::OnUpdate() { + if (!is_activated) { + return; + } + + if (!device_enabled) { + return; + } + + if (!polling_mode_enabled || !is_transfer_memory_set) { + return; + } + + // TODO: Increment multitasking counters from motion and sensor data + + switch (polling_mode) { + case JoyPollingMode::SixAxisSensorEnable: { + enable_sixaxis_data.header.total_entries = 10; + enable_sixaxis_data.header.result = ResultSuccess; + const auto& last_entry = + enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry]; + + enable_sixaxis_data.header.latest_entry = + (enable_sixaxis_data.header.latest_entry + 1) % 10; + auto& curr_entry = enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry]; + + curr_entry.sampling_number = last_entry.sampling_number + 1; + curr_entry.polling_data.sampling_number = curr_entry.sampling_number; + + const RingConData ringcon_value = GetSensorValue(); + curr_entry.polling_data.out_size = sizeof(ringcon_value); + std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value)); + + std::memcpy(transfer_memory, &enable_sixaxis_data, sizeof(enable_sixaxis_data)); + break; + } + default: + LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode); + break; + } +} + +RingController::RingConData RingController::GetSensorValue() const { + RingConData ringcon_sensor_value{ + .status = DataValid::Valid, + .data = 0, + }; + + const f32 force_value = input->GetRingSensorForce().force * range; + ringcon_sensor_value.data = static_cast<s16>(force_value) + idle_value; + + return ringcon_sensor_value; +} + +u8 RingController::GetDeviceId() const { + return device_id; +} + +std::vector<u8> RingController::GetReply() const { + const RingConCommands current_command = command; + + switch (current_command) { + case RingConCommands::GetFirmwareVersion: + return GetFirmwareVersionReply(); + case RingConCommands::ReadId: + return GetReadIdReply(); + case RingConCommands::c20105: + return GetC020105Reply(); + case RingConCommands::ReadUnkCal: + return GetReadUnkCalReply(); + case RingConCommands::ReadFactoryCal: + return GetReadFactoryCalReply(); + case RingConCommands::ReadUserCal: + return GetReadUserCalReply(); + case RingConCommands::ReadRepCount: + return GetReadRepCountReply(); + case RingConCommands::ReadTotalPushCount: + return GetReadTotalPushCountReply(); + case RingConCommands::ResetRepCount: + return GetResetRepCountReply(); + case RingConCommands::SaveCalData: + return GetSaveDataReply(); + default: + return GetErrorReply(); + } +} + +bool RingController::SetCommand(const std::vector<u8>& data) { + if (data.size() < 4) { + LOG_ERROR(Service_HID, "Command size not supported {}", data.size()); + command = RingConCommands::Error; + return false; + } + + std::memcpy(&command, data.data(), sizeof(RingConCommands)); + + switch (command) { + case RingConCommands::GetFirmwareVersion: + case RingConCommands::ReadId: + case RingConCommands::c20105: + case RingConCommands::ReadUnkCal: + case RingConCommands::ReadFactoryCal: + case RingConCommands::ReadUserCal: + case RingConCommands::ReadRepCount: + case RingConCommands::ReadTotalPushCount: + ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes"); + send_command_async_event->GetWritableEvent().Signal(); + return true; + case RingConCommands::ResetRepCount: + ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes"); + total_rep_count = 0; + send_command_async_event->GetWritableEvent().Signal(); + return true; + case RingConCommands::SaveCalData: { + ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes"); + + SaveCalData save_info{}; + std::memcpy(&save_info, data.data(), sizeof(SaveCalData)); + user_calibration = save_info.calibration; + send_command_async_event->GetWritableEvent().Signal(); + return true; + } + default: + LOG_ERROR(Service_HID, "Command not implemented {}", command); + command = RingConCommands::Error; + // Signal a reply to avoid softlocking the game + send_command_async_event->GetWritableEvent().Signal(); + return false; + } +} + +std::vector<u8> RingController::GetFirmwareVersionReply() const { + const FirmwareVersionReply reply{ + .status = DataValid::Valid, + .firmware = version, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadIdReply() const { + // The values are hardcoded from a real joycon + const ReadIdReply reply{ + .status = DataValid::Valid, + .id_l_x0 = 8, + .id_l_x0_2 = 41, + .id_l_x4 = 22294, + .id_h_x0 = 19777, + .id_h_x0_2 = 13621, + .id_h_x4 = 8245, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetC020105Reply() const { + const Cmd020105Reply reply{ + .status = DataValid::Valid, + .data = 1, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadUnkCalReply() const { + const ReadUnkCalReply reply{ + .status = DataValid::Valid, + .data = 0, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadFactoryCalReply() const { + const ReadFactoryCalReply reply{ + .status = DataValid::Valid, + .calibration = factory_calibration, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadUserCalReply() const { + const ReadUserCalReply reply{ + .status = DataValid::Valid, + .calibration = user_calibration, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadRepCountReply() const { + const GetThreeByteReply reply{ + .status = DataValid::Valid, + .data = {total_rep_count, 0, 0}, + .crc = GetCrcValue({total_rep_count, 0, 0, 0}), + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadTotalPushCountReply() const { + const GetThreeByteReply reply{ + .status = DataValid::Valid, + .data = {total_push_count, 0, 0}, + .crc = GetCrcValue({total_push_count, 0, 0, 0}), + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetResetRepCountReply() const { + return GetReadRepCountReply(); +} + +std::vector<u8> RingController::GetSaveDataReply() const { + const StatusReply reply{ + .status = DataValid::Valid, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetErrorReply() const { + const ErrorReply reply{ + .status = DataValid::BadCRC, + }; + + return GetDataVector(reply); +} + +u8 RingController::GetCrcValue(const std::vector<u8>& data) const { + u8 crc = 0; + for (std::size_t index = 0; index < data.size(); index++) { + for (u8 i = 0x80; i > 0; i >>= 1) { + bool bit = (crc & 0x80) != 0; + if ((data[index] & i) != 0) { + bit = !bit; + } + crc <<= 1; + if (bit) { + crc ^= 0x8d; + } + } + } + return crc; +} + +template <typename T> +std::vector<u8> RingController::GetDataVector(const T& reply) const { + static_assert(std::is_trivially_copyable_v<T>); + std::vector<u8> data; + data.resize(sizeof(reply)); + std::memcpy(data.data(), &reply, sizeof(reply)); + return data; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/ringcon.h b/src/core/hle/service/hid/hidbus/ringcon.h new file mode 100644 index 000000000..b37df50ac --- /dev/null +++ b/src/core/hle/service/hid/hidbus/ringcon.h @@ -0,0 +1,253 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "common/common_types.h" +#include "core/hle/service/hid/hidbus/hidbus_base.h" + +namespace Core::HID { +class EmulatedDevices; +} // namespace Core::HID + +namespace Service::HID { + +class RingController final : public HidbusBase { +public: + explicit RingController(Core::HID::HIDCore& hid_core_, + KernelHelpers::ServiceContext& service_context_); + ~RingController() override; + + void OnInit() override; + + void OnRelease() override; + + // Updates ringcon transfer memory + void OnUpdate() override; + + // Returns the device ID of the joycon + u8 GetDeviceId() const override; + + // Assigns a command from data + bool SetCommand(const std::vector<u8>& data) override; + + // Returns a reply from a command + std::vector<u8> GetReply() const override; + +private: + // These values are obtained from a real ring controller + static constexpr s16 idle_value = 2280; + static constexpr s16 idle_deadzone = 120; + static constexpr s16 range = 2500; + + // Most missing command names are leftovers from other firmware versions + enum class RingConCommands : u32 { + GetFirmwareVersion = 0x00020000, + ReadId = 0x00020100, + JoyPolling = 0x00020101, + Unknown1 = 0x00020104, + c20105 = 0x00020105, + Unknown2 = 0x00020204, + Unknown3 = 0x00020304, + Unknown4 = 0x00020404, + ReadUnkCal = 0x00020504, + ReadFactoryCal = 0x00020A04, + Unknown5 = 0x00021104, + Unknown6 = 0x00021204, + Unknown7 = 0x00021304, + ReadUserCal = 0x00021A04, + ReadRepCount = 0x00023104, + ReadTotalPushCount = 0x00023204, + ResetRepCount = 0x04013104, + Unknown8 = 0x04011104, + Unknown9 = 0x04011204, + Unknown10 = 0x04011304, + SaveCalData = 0x10011A04, + Error = 0xFFFFFFFF, + }; + + enum class DataValid : u32 { + Valid, + BadCRC, + Cal, + }; + + struct FirmwareVersion { + u8 sub; + u8 main; + }; + static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size"); + + struct FactoryCalibration { + s32_le os_max; + s32_le hk_max; + s32_le zero_min; + s32_le zero_max; + }; + static_assert(sizeof(FactoryCalibration) == 0x10, "FactoryCalibration is an invalid size"); + + struct CalibrationValue { + s16 value; + u16 crc; + }; + static_assert(sizeof(CalibrationValue) == 0x4, "CalibrationValue is an invalid size"); + + struct UserCalibration { + CalibrationValue os_max; + CalibrationValue hk_max; + CalibrationValue zero; + }; + static_assert(sizeof(UserCalibration) == 0xC, "UserCalibration is an invalid size"); + + struct SaveCalData { + RingConCommands command; + UserCalibration calibration; + INSERT_PADDING_BYTES_NOINIT(4); + }; + static_assert(sizeof(SaveCalData) == 0x14, "SaveCalData is an invalid size"); + static_assert(std::is_trivially_copyable_v<SaveCalData>, + "SaveCalData must be trivially copyable"); + + struct FirmwareVersionReply { + DataValid status; + FirmwareVersion firmware; + INSERT_PADDING_BYTES(0x2); + }; + static_assert(sizeof(FirmwareVersionReply) == 0x8, "FirmwareVersionReply is an invalid size"); + + struct Cmd020105Reply { + DataValid status; + u8 data; + INSERT_PADDING_BYTES(0x3); + }; + static_assert(sizeof(Cmd020105Reply) == 0x8, "Cmd020105Reply is an invalid size"); + + struct StatusReply { + DataValid status; + }; + static_assert(sizeof(StatusReply) == 0x4, "StatusReply is an invalid size"); + + struct GetThreeByteReply { + DataValid status; + std::array<u8, 3> data; + u8 crc; + }; + static_assert(sizeof(GetThreeByteReply) == 0x8, "GetThreeByteReply is an invalid size"); + + struct ReadUnkCalReply { + DataValid status; + u16 data; + INSERT_PADDING_BYTES(0x2); + }; + static_assert(sizeof(ReadUnkCalReply) == 0x8, "ReadUnkCalReply is an invalid size"); + + struct ReadFactoryCalReply { + DataValid status; + FactoryCalibration calibration; + }; + static_assert(sizeof(ReadFactoryCalReply) == 0x14, "ReadFactoryCalReply is an invalid size"); + + struct ReadUserCalReply { + DataValid status; + UserCalibration calibration; + INSERT_PADDING_BYTES(0x4); + }; + static_assert(sizeof(ReadUserCalReply) == 0x14, "ReadUserCalReply is an invalid size"); + + struct ReadIdReply { + DataValid status; + u16 id_l_x0; + u16 id_l_x0_2; + u16 id_l_x4; + u16 id_h_x0; + u16 id_h_x0_2; + u16 id_h_x4; + }; + static_assert(sizeof(ReadIdReply) == 0x10, "ReadIdReply is an invalid size"); + + struct ErrorReply { + DataValid status; + INSERT_PADDING_BYTES(0x3); + }; + static_assert(sizeof(ErrorReply) == 0x8, "ErrorReply is an invalid size"); + + struct RingConData { + DataValid status; + s16_le data; + INSERT_PADDING_BYTES(0x2); + }; + static_assert(sizeof(RingConData) == 0x8, "RingConData is an invalid size"); + + // Returns RingConData struct with pressure sensor values + RingConData GetSensorValue() const; + + // Returns 8 byte reply with firmware version + std::vector<u8> GetFirmwareVersionReply() const; + + // Returns 16 byte reply with ID values + std::vector<u8> GetReadIdReply() const; + + // (STUBBED) Returns 8 byte reply + std::vector<u8> GetC020105Reply() const; + + // (STUBBED) Returns 8 byte empty reply + std::vector<u8> GetReadUnkCalReply() const; + + // Returns 20 byte reply with factory calibration values + std::vector<u8> GetReadFactoryCalReply() const; + + // Returns 20 byte reply with user calibration values + std::vector<u8> GetReadUserCalReply() const; + + // Returns 8 byte reply + std::vector<u8> GetReadRepCountReply() const; + + // Returns 8 byte reply + std::vector<u8> GetReadTotalPushCountReply() const; + + // Returns 8 byte reply + std::vector<u8> GetResetRepCountReply() const; + + // Returns 4 byte save data reply + std::vector<u8> GetSaveDataReply() const; + + // Returns 8 byte error reply + std::vector<u8> GetErrorReply() const; + + // Returns 8 bit redundancy check from provided data + u8 GetCrcValue(const std::vector<u8>& data) const; + + // Converts structs to an u8 vector equivalent + template <typename T> + std::vector<u8> GetDataVector(const T& reply) const; + + RingConCommands command{RingConCommands::Error}; + + // These counters are used in multitasking mode while the switch is sleeping + // Total steps taken + u8 total_rep_count = 0; + // Total times the ring was pushed + u8 total_push_count = 0; + + const u8 device_id = 0x20; + const FirmwareVersion version = { + .sub = 0x0, + .main = 0x2c, + }; + const FactoryCalibration factory_calibration = { + .os_max = idle_value + range + idle_deadzone, + .hk_max = idle_value - range - idle_deadzone, + .zero_min = idle_value - idle_deadzone, + .zero_max = idle_value + idle_deadzone, + }; + UserCalibration user_calibration = { + .os_max = {.value = range, .crc = 228}, + .hk_max = {.value = -range, .crc = 239}, + .zero = {.value = idle_value, .crc = 225}, + }; + + Core::HID::EmulatedDevices* input; +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/starlink.cpp b/src/core/hle/service/hid/hidbus/starlink.cpp new file mode 100644 index 000000000..dd439f60a --- /dev/null +++ b/src/core/hle/service/hid/hidbus/starlink.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hle/service/hid/hidbus/starlink.h" + +namespace Service::HID { +constexpr u8 DEVICE_ID = 0x28; + +Starlink::Starlink(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_) + : HidbusBase(service_context_) {} +Starlink::~Starlink() = default; + +void Starlink::OnInit() { + return; +} + +void Starlink::OnRelease() { + return; +}; + +void Starlink::OnUpdate() { + if (!is_activated) { + return; + } + if (!device_enabled) { + return; + } + if (!polling_mode_enabled || !is_transfer_memory_set) { + return; + } + + LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode); +} + +u8 Starlink::GetDeviceId() const { + return DEVICE_ID; +} + +std::vector<u8> Starlink::GetReply() const { + return {}; +} + +bool Starlink::SetCommand(const std::vector<u8>& data) { + LOG_ERROR(Service_HID, "Command not implemented"); + return false; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/starlink.h b/src/core/hle/service/hid/hidbus/starlink.h new file mode 100644 index 000000000..0b1b7ba49 --- /dev/null +++ b/src/core/hle/service/hid/hidbus/starlink.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/service/hid/hidbus/hidbus_base.h" + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::HID { + +class Starlink final : public HidbusBase { +public: + explicit Starlink(Core::HID::HIDCore& hid_core_, + KernelHelpers::ServiceContext& service_context_); + ~Starlink() override; + + void OnInit() override; + + void OnRelease() override; + + // Updates ringcon transfer memory + void OnUpdate() override; + + // Returns the device ID of the joycon + u8 GetDeviceId() const override; + + // Assigns a command from data + bool SetCommand(const std::vector<u8>& data) override; + + // Returns a reply from a command + std::vector<u8> GetReply() const override; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/stubbed.cpp b/src/core/hle/service/hid/hidbus/stubbed.cpp new file mode 100644 index 000000000..e477443e3 --- /dev/null +++ b/src/core/hle/service/hid/hidbus/stubbed.cpp @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hle/service/hid/hidbus/stubbed.h" + +namespace Service::HID { +constexpr u8 DEVICE_ID = 0xFF; + +HidbusStubbed::HidbusStubbed(Core::HID::HIDCore& hid_core_, + KernelHelpers::ServiceContext& service_context_) + : HidbusBase(service_context_) {} +HidbusStubbed::~HidbusStubbed() = default; + +void HidbusStubbed::OnInit() { + return; +} + +void HidbusStubbed::OnRelease() { + return; +}; + +void HidbusStubbed::OnUpdate() { + if (!is_activated) { + return; + } + if (!device_enabled) { + return; + } + if (!polling_mode_enabled || !is_transfer_memory_set) { + return; + } + + LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode); +} + +u8 HidbusStubbed::GetDeviceId() const { + return DEVICE_ID; +} + +std::vector<u8> HidbusStubbed::GetReply() const { + return {}; +} + +bool HidbusStubbed::SetCommand(const std::vector<u8>& data) { + LOG_ERROR(Service_HID, "Command not implemented"); + return false; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/stubbed.h b/src/core/hle/service/hid/hidbus/stubbed.h new file mode 100644 index 000000000..91165ceff --- /dev/null +++ b/src/core/hle/service/hid/hidbus/stubbed.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/service/hid/hidbus/hidbus_base.h" + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::HID { + +class HidbusStubbed final : public HidbusBase { +public: + explicit HidbusStubbed(Core::HID::HIDCore& hid_core_, + KernelHelpers::ServiceContext& service_context_); + ~HidbusStubbed() override; + + void OnInit() override; + + void OnRelease() override; + + // Updates ringcon transfer memory + void OnUpdate() override; + + // Returns the device ID of the joycon + u8 GetDeviceId() const override; + + // Assigns a command from data + bool SetCommand(const std::vector<u8>& data) override; + + // Returns a reply from a command + std::vector<u8> GetReply() const override; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 8812b8ecb..6a3453457 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -1,15 +1,28 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <random> #include "core/core.h" #include "core/core_timing.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/service/hid/errors.h" #include "core/hle/service/hid/irs.h" +#include "core/hle/service/hid/irsensor/clustering_processor.h" +#include "core/hle/service/hid/irsensor/image_transfer_processor.h" +#include "core/hle/service/hid/irsensor/ir_led_processor.h" +#include "core/hle/service/hid/irsensor/moment_processor.h" +#include "core/hle/service/hid/irsensor/pointing_processor.h" +#include "core/hle/service/hid/irsensor/tera_plugin_processor.h" +#include "core/memory.h" -namespace Service::HID { +namespace Service::IRS { IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { // clang-format off @@ -35,25 +48,41 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { }; // clang-format on + u8* raw_shared_memory = system.Kernel().GetIrsSharedMem().GetPointer(); RegisterHandlers(functions); + shared_memory = std::construct_at(reinterpret_cast<StatusManager*>(raw_shared_memory)); + + npad_device = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); } +IRS::~IRS() = default; void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_IRS, "called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); @@ -61,114 +90,462 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) { } void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); + + auto result = IsIrCameraHandleValid(parameters.camera_handle); + if (result.IsSuccess()) { + // TODO: Stop Image processor + result = ResultSuccess; + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + Core::IrSensor::PackedMomentProcessorConfig processor_config; + }; + static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); + + const auto result = IsIrCameraHandleValid(parameters.camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + MakeProcessor<MomentProcessor>(parameters.camera_handle, device); + auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); + image_transfer_processor.SetConfig(parameters.processor_config); + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + Core::IrSensor::PackedClusteringProcessorConfig processor_config; + }; + static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); + + auto result = IsIrCameraHandleValid(parameters.camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + MakeProcessorWithCoreContext<ClusteringProcessor>(parameters.camera_handle, device); + auto& image_transfer_processor = + GetProcessor<ClusteringProcessor>(parameters.camera_handle); + image_transfer_processor.SetConfig(parameters.processor_config); + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + Core::IrSensor::PackedImageTransferProcessorConfig processor_config; + u32 transfer_memory_size; + }; + static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + const auto t_mem_handle{ctx.GetCopyHandle(0)}; + + auto t_mem = + system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); + + if (t_mem.IsNull()) { + LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size"); + + u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress()); + + LOG_INFO(Service_IRS, + "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, " + "applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id); + + const auto result = IsIrCameraHandleValid(parameters.camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); + auto& image_transfer_processor = + GetProcessor<ImageTransferProcessor>(parameters.camera_handle); + image_transfer_processor.SetConfig(parameters.processor_config); + image_transfer_processor.SetTransferMemoryPointer(transfer_memory); + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); + + const auto result = IsIrCameraHandleValid(parameters.camera_handle); + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + + if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidProcessorState); + return; + } - IPC::ResponseBuilder rb{ctx, 5}; + std::vector<u8> data{}; + const auto& image_transfer_processor = + GetProcessor<ImageTransferProcessor>(parameters.camera_handle); + const auto& state = image_transfer_processor.GetState(data); + + ctx.WriteBuffer(data); + IPC::ResponseBuilder rb{ctx, 6}; rb.Push(ResultSuccess); - rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks()); - rb.PushRaw<u32>(0); + rb.PushRaw(state); } void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + Core::IrSensor::PackedTeraPluginProcessorConfig processor_config; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING( + Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, " + "applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major, + parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id); + + const auto result = IsIrCameraHandleValid(parameters.camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device); + auto& image_transfer_processor = + GetProcessor<TeraPluginProcessor>(parameters.camera_handle); + image_transfer_processor.SetConfig(parameters.processor_config); + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; + + if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid && + npad_id != Core::HID::NpadIdType::Handheld) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(Service::HID::InvalidNpadId); + return; + } + + Core::IrSensor::IrCameraHandle camera_handle{ + .npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)), + .npad_type = Core::HID::NpadStyleIndex::None, + }; + + LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id, + camera_handle.npad_id, camera_handle.npad_type); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushRaw<u32>(device_handle); + rb.PushRaw(camera_handle); } void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; + const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING( + Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}", + camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major, + processor_config.required_mcu_version.minor, applet_resource_user_id); + + auto result = IsIrCameraHandleValid(camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); + MakeProcessor<PointingProcessor>(camera_handle, device); + auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); + image_transfer_processor.SetConfig(processor_config); + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); + + auto result = IsIrCameraHandleValid(parameters.camera_handle); + if (result.IsSuccess()) { + // TODO: Suspend image processor + result = ResultSuccess; + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; + const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING( + Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}", + camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major, + mcu_version.minor); + + auto result = IsIrCameraHandleValid(camera_handle); + if (result.IsSuccess()) { + // TODO: Check firmware version + result = ResultSuccess; + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; + const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING( + Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}", + camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, + applet_resource_user_id); + + auto result = IsIrCameraHandleValid(camera_handle); + if (result.IsSuccess()) { + // TODO: Set Function level + result = ResultSuccess; + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + Core::IrSensor::PackedImageTransferProcessorExConfig processor_config; + u64 transfer_memory_size; + }; + static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + const auto t_mem_handle{ctx.GetCopyHandle(0)}; + + auto t_mem = + system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); + + u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress()); + + LOG_INFO(Service_IRS, + "called, npad_type={}, npad_id={}, transfer_memory_size={}, " + "applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.transfer_memory_size, parameters.applet_resource_user_id); + + auto result = IsIrCameraHandleValid(parameters.camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); + auto& image_transfer_processor = + GetProcessor<ImageTransferProcessor>(parameters.camera_handle); + image_transfer_processor.SetConfig(parameters.processor_config); + image_transfer_processor.SetTransferMemoryPointer(transfer_memory); + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; + const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} " + "applet_resource_user_id={}", + camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target, + processor_config.required_mcu_version.major, + processor_config.required_mcu_version.minor, applet_resource_user_id); + + auto result = IsIrCameraHandleValid(camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); + MakeProcessor<IrLedProcessor>(camera_handle, device); + auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); + image_transfer_processor.SetConfig(processor_config); + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); + + auto result = IsIrCameraHandleValid(parameters.camera_handle); + if (result.IsSuccess()) { + // TODO: Stop image processor async + result = ResultSuccess; + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::PackedFunctionLevel function_level; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}", + parameters.function_level.function_level, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } -IRS::~IRS() = default; +Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const { + if (camera_handle.npad_id > + static_cast<u8>(NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) { + return InvalidIrCameraHandle; + } + if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) { + return InvalidIrCameraHandle; + } + return ResultSuccess; +} + +Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry( + const Core::IrSensor::IrCameraHandle& camera_handle) { + const auto npad_id_max_index = static_cast<u8>(sizeof(StatusManager::device)); + ASSERT_MSG(camera_handle.npad_id < npad_id_max_index, "invalid npad_id"); + return shared_memory->device[camera_handle.npad_id]; +} IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} { // clang-format off @@ -185,4 +562,4 @@ IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} { IRS_SYS::~IRS_SYS() = default; -} // namespace Service::HID +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h index 9bc6462b0..2e6115c73 100644 --- a/src/core/hle/service/hid/irs.h +++ b/src/core/hle/service/hid/irs.h @@ -1,16 +1,22 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "core/hid/hid_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" #include "core/hle/service/service.h" namespace Core { class System; } -namespace Service::HID { +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::IRS { class IRS final : public ServiceFramework<IRS> { public: @@ -18,6 +24,20 @@ public: ~IRS() override; private: + // This is nn::irsensor::detail::AruidFormat + struct AruidFormat { + u64 sensor_aruid; + u64 sensor_aruid_status; + }; + static_assert(sizeof(AruidFormat) == 0x10, "AruidFormat is an invalid size"); + + // This is nn::irsensor::detail::StatusManager + struct StatusManager { + std::array<Core::IrSensor::DeviceFormat, 9> device; + std::array<AruidFormat, 5> aruid; + }; + static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size"); + void ActivateIrsensor(Kernel::HLERequestContext& ctx); void DeactivateIrsensor(Kernel::HLERequestContext& ctx); void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx); @@ -37,7 +57,55 @@ private: void StopImageProcessorAsync(Kernel::HLERequestContext& ctx); void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx); - const u32 device_handle{0xABCD}; + Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const; + Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry( + const Core::IrSensor::IrCameraHandle& camera_handle); + + template <typename T> + void MakeProcessor(const Core::IrSensor::IrCameraHandle& handle, + Core::IrSensor::DeviceFormat& device_state) { + const auto index = static_cast<std::size_t>(handle.npad_id); + if (index > sizeof(processors)) { + LOG_CRITICAL(Service_IRS, "Invalid index {}", index); + return; + } + processors[index] = std::make_unique<T>(device_state); + } + + template <typename T> + void MakeProcessorWithCoreContext(const Core::IrSensor::IrCameraHandle& handle, + Core::IrSensor::DeviceFormat& device_state) { + const auto index = static_cast<std::size_t>(handle.npad_id); + if (index > sizeof(processors)) { + LOG_CRITICAL(Service_IRS, "Invalid index {}", index); + return; + } + processors[index] = std::make_unique<T>(system.HIDCore(), device_state, index); + } + + template <typename T> + T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) { + const auto index = static_cast<std::size_t>(handle.npad_id); + if (index > sizeof(processors)) { + LOG_CRITICAL(Service_IRS, "Invalid index {}", index); + return static_cast<T&>(*processors[0]); + } + return static_cast<T&>(*processors[index]); + } + + template <typename T> + const T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) const { + const auto index = static_cast<std::size_t>(handle.npad_id); + if (index > sizeof(processors)) { + LOG_CRITICAL(Service_IRS, "Invalid index {}", index); + return static_cast<T&>(*processors[0]); + } + return static_cast<T&>(*processors[index]); + } + + Core::HID::EmulatedController* npad_device = nullptr; + StatusManager* shared_memory = nullptr; + std::array<std::unique_ptr<ProcessorBase>, 9> processors{}; }; class IRS_SYS final : public ServiceFramework<IRS_SYS> { @@ -46,4 +114,4 @@ public: ~IRS_SYS() override; }; -} // namespace Service::HID +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irs_ring_lifo.h b/src/core/hle/service/hid/irs_ring_lifo.h new file mode 100644 index 000000000..255d1d296 --- /dev/null +++ b/src/core/hle/service/hid/irs_ring_lifo.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <array> + +#include "common/common_types.h" + +namespace Service::IRS { + +template <typename State, std::size_t max_buffer_size> +struct Lifo { + s64 sampling_number{}; + s64 buffer_count{}; + std::array<State, max_buffer_size> entries{}; + + const State& ReadCurrentEntry() const { + return entries[GetBufferTail()]; + } + + const State& ReadPreviousEntry() const { + return entries[GetPreviousEntryIndex()]; + } + + s64 GetBufferTail() const { + return sampling_number % max_buffer_size; + } + + std::size_t GetPreviousEntryIndex() const { + return static_cast<size_t>((GetBufferTail() + max_buffer_size - 1) % max_buffer_size); + } + + std::size_t GetNextEntryIndex() const { + return static_cast<size_t>((GetBufferTail() + 1) % max_buffer_size); + } + + void WriteNextEntry(const State& new_state) { + if (buffer_count < static_cast<s64>(max_buffer_size)) { + buffer_count++; + } + sampling_number++; + entries[GetBufferTail()] = new_state; + } +}; + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.cpp b/src/core/hle/service/hid/irsensor/clustering_processor.cpp new file mode 100644 index 000000000..e2f4ae876 --- /dev/null +++ b/src/core/hle/service/hid/irsensor/clustering_processor.cpp @@ -0,0 +1,265 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <queue> + +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hle/service/hid/irsensor/clustering_processor.h" + +namespace Service::IRS { +ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_, + Core::IrSensor::DeviceFormat& device_format, + std::size_t npad_index) + : device{device_format} { + npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index); + + device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; + SetDefaultConfig(); + + shared_memory = std::construct_at( + reinterpret_cast<ClusteringSharedMemory*>(&device_format.state.processor_raw_data)); + + Core::HID::ControllerUpdateCallback engine_callback{ + .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); }, + .is_npad_service = true, + }; + callback_key = npad_device->SetCallback(engine_callback); +} + +ClusteringProcessor::~ClusteringProcessor() { + npad_device->DeleteCallback(callback_key); +}; + +void ClusteringProcessor::StartProcessor() { + device.camera_status = Core::IrSensor::IrCameraStatus::Available; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready; +} + +void ClusteringProcessor::SuspendProcessor() {} + +void ClusteringProcessor::StopProcessor() {} + +void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) { + if (type != Core::HID::ControllerTriggerType::IrSensor) { + return; + } + + next_state = {}; + const auto camera_data = npad_device->GetCamera(); + auto filtered_image = camera_data.data; + + RemoveLowIntensityData(filtered_image); + + const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x); + const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y); + const auto window_end_x = + window_start_x + static_cast<std::size_t>(current_config.window_of_interest.width); + const auto window_end_y = + window_start_y + static_cast<std::size_t>(current_config.window_of_interest.height); + + for (std::size_t y = window_start_y; y < window_end_y; y++) { + for (std::size_t x = window_start_x; x < window_end_x; x++) { + u8 pixel = GetPixel(filtered_image, x, y); + if (pixel == 0) { + continue; + } + const auto cluster = GetClusterProperties(filtered_image, x, y); + if (cluster.pixel_count > current_config.pixel_count_max) { + continue; + } + if (cluster.pixel_count < current_config.pixel_count_min) { + continue; + } + // Cluster object limit reached + if (next_state.object_count >= next_state.data.size()) { + continue; + } + next_state.data[next_state.object_count] = cluster; + next_state.object_count++; + } + } + + next_state.sampling_number = camera_data.sample; + next_state.timestamp = next_state.timestamp + 131; + next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; + shared_memory->clustering_lifo.WriteNextEntry(next_state); + + if (!IsProcessorActive()) { + StartProcessor(); + } +} + +void ClusteringProcessor::RemoveLowIntensityData(std::vector<u8>& data) { + for (u8& pixel : data) { + if (pixel < current_config.pixel_count_min) { + pixel = 0; + } + } +} + +ClusteringProcessor::ClusteringData ClusteringProcessor::GetClusterProperties(std::vector<u8>& data, + std::size_t x, + std::size_t y) { + using DataPoint = Common::Point<std::size_t>; + std::queue<DataPoint> search_points{}; + ClusteringData current_cluster = GetPixelProperties(data, x, y); + SetPixel(data, x, y, 0); + search_points.emplace<DataPoint>({x, y}); + + while (!search_points.empty()) { + const auto point = search_points.front(); + search_points.pop(); + + // Avoid negative numbers + if (point.x == 0 || point.y == 0) { + continue; + } + + std::array<DataPoint, 4> new_points{ + DataPoint{point.x - 1, point.y}, + {point.x, point.y - 1}, + {point.x + 1, point.y}, + {point.x, point.y + 1}, + }; + + for (const auto new_point : new_points) { + if (new_point.x >= width) { + continue; + } + if (new_point.y >= height) { + continue; + } + if (GetPixel(data, new_point.x, new_point.y) < current_config.object_intensity_min) { + continue; + } + const ClusteringData cluster = GetPixelProperties(data, new_point.x, new_point.y); + current_cluster = MergeCluster(current_cluster, cluster); + SetPixel(data, new_point.x, new_point.y, 0); + search_points.emplace<DataPoint>({new_point.x, new_point.y}); + } + } + + return current_cluster; +} + +ClusteringProcessor::ClusteringData ClusteringProcessor::GetPixelProperties( + const std::vector<u8>& data, std::size_t x, std::size_t y) const { + return { + .average_intensity = GetPixel(data, x, y) / 255.0f, + .centroid = + { + .x = static_cast<f32>(x), + .y = static_cast<f32>(y), + + }, + .pixel_count = 1, + .bound = + { + .x = static_cast<s16>(x), + .y = static_cast<s16>(y), + .width = 1, + .height = 1, + }, + }; +} + +ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster( + const ClusteringData a, const ClusteringData b) const { + const f32 a_pixel_count = static_cast<f32>(a.pixel_count); + const f32 b_pixel_count = static_cast<f32>(b.pixel_count); + const f32 pixel_count = a_pixel_count + b_pixel_count; + const f32 average_intensity = + (a.average_intensity * a_pixel_count + b.average_intensity * b_pixel_count) / pixel_count; + const Core::IrSensor::IrsCentroid centroid = { + .x = (a.centroid.x * a_pixel_count + b.centroid.x * b_pixel_count) / pixel_count, + .y = (a.centroid.y * a_pixel_count + b.centroid.y * b_pixel_count) / pixel_count, + }; + s16 bound_start_x = a.bound.x < b.bound.x ? a.bound.x : b.bound.x; + s16 bound_start_y = a.bound.y < b.bound.y ? a.bound.y : b.bound.y; + s16 a_bound_end_x = a.bound.x + a.bound.width; + s16 a_bound_end_y = a.bound.y + a.bound.height; + s16 b_bound_end_x = b.bound.x + b.bound.width; + s16 b_bound_end_y = b.bound.y + b.bound.height; + + const Core::IrSensor::IrsRect bound = { + .x = bound_start_x, + .y = bound_start_y, + .width = a_bound_end_x > b_bound_end_x ? static_cast<s16>(a_bound_end_x - bound_start_x) + : static_cast<s16>(b_bound_end_x - bound_start_x), + .height = a_bound_end_y > b_bound_end_y ? static_cast<s16>(a_bound_end_y - bound_start_y) + : static_cast<s16>(b_bound_end_y - bound_start_y), + }; + + return { + .average_intensity = average_intensity, + .centroid = centroid, + .pixel_count = static_cast<u32>(pixel_count), + .bound = bound, + }; +} + +u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const { + if ((y * width) + x > data.size()) { + return 0; + } + return data[(y * width) + x]; +} + +void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) { + if ((y * width) + x > data.size()) { + return; + } + data[(y * width) + x] = value; +} + +void ClusteringProcessor::SetDefaultConfig() { + using namespace std::literals::chrono_literals; + current_config.camera_config.exposure_time = std::chrono::microseconds(200ms).count(); + current_config.camera_config.gain = 2; + current_config.camera_config.is_negative_used = false; + current_config.camera_config.light_target = Core::IrSensor::CameraLightTarget::BrightLeds; + current_config.window_of_interest = { + .x = 0, + .y = 0, + .width = width, + .height = height, + }; + current_config.pixel_count_min = 3; + current_config.pixel_count_max = static_cast<u32>(GetDataSize(format)); + current_config.is_external_light_filter_enabled = true; + current_config.object_intensity_min = 150; + + npad_device->SetCameraFormat(format); +} + +void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) { + current_config.camera_config.exposure_time = config.camera_config.exposure_time; + current_config.camera_config.gain = config.camera_config.gain; + current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; + current_config.camera_config.light_target = + static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); + current_config.window_of_interest = config.window_of_interest; + current_config.pixel_count_min = config.pixel_count_min; + current_config.pixel_count_max = config.pixel_count_max; + current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled; + current_config.object_intensity_min = config.object_intensity_min; + + LOG_INFO(Service_IRS, + "Processor config, exposure_time={}, gain={}, is_negative_used={}, " + "light_target={}, window_of_interest=({}, {}, {}, {}), pixel_count_min={}, " + "pixel_count_max={}, is_external_light_filter_enabled={}, object_intensity_min={}", + current_config.camera_config.exposure_time, current_config.camera_config.gain, + current_config.camera_config.is_negative_used, + current_config.camera_config.light_target, current_config.window_of_interest.x, + current_config.window_of_interest.y, current_config.window_of_interest.width, + current_config.window_of_interest.height, current_config.pixel_count_min, + current_config.pixel_count_max, current_config.is_external_light_filter_enabled, + current_config.object_intensity_min); + + npad_device->SetCameraFormat(format); +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.h b/src/core/hle/service/hid/irsensor/clustering_processor.h new file mode 100644 index 000000000..dc01a8ea7 --- /dev/null +++ b/src/core/hle/service/hid/irsensor/clustering_processor.h @@ -0,0 +1,110 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irs_ring_lifo.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::IRS { +class ClusteringProcessor final : public ProcessorBase { +public: + explicit ClusteringProcessor(Core::HID::HIDCore& hid_core_, + Core::IrSensor::DeviceFormat& device_format, + std::size_t npad_index); + ~ClusteringProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config); + +private: + static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size320x240; + static constexpr std::size_t width = 320; + static constexpr std::size_t height = 240; + + // This is nn::irsensor::ClusteringProcessorConfig + struct ClusteringProcessorConfig { + Core::IrSensor::CameraConfig camera_config; + Core::IrSensor::IrsRect window_of_interest; + u32 pixel_count_min; + u32 pixel_count_max; + u32 object_intensity_min; + bool is_external_light_filter_enabled; + INSERT_PADDING_BYTES(3); + }; + static_assert(sizeof(ClusteringProcessorConfig) == 0x30, + "ClusteringProcessorConfig is an invalid size"); + + // This is nn::irsensor::AdaptiveClusteringProcessorConfig + struct AdaptiveClusteringProcessorConfig { + Core::IrSensor::AdaptiveClusteringMode mode; + Core::IrSensor::AdaptiveClusteringTargetDistance target_distance; + }; + static_assert(sizeof(AdaptiveClusteringProcessorConfig) == 0x8, + "AdaptiveClusteringProcessorConfig is an invalid size"); + + // This is nn::irsensor::ClusteringData + struct ClusteringData { + f32 average_intensity; + Core::IrSensor::IrsCentroid centroid; + u32 pixel_count; + Core::IrSensor::IrsRect bound; + }; + static_assert(sizeof(ClusteringData) == 0x18, "ClusteringData is an invalid size"); + + // This is nn::irsensor::ClusteringProcessorState + struct ClusteringProcessorState { + s64 sampling_number; + u64 timestamp; + u8 object_count; + INSERT_PADDING_BYTES(3); + Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; + std::array<ClusteringData, 0x10> data; + }; + static_assert(sizeof(ClusteringProcessorState) == 0x198, + "ClusteringProcessorState is an invalid size"); + + struct ClusteringSharedMemory { + Service::IRS::Lifo<ClusteringProcessorState, 6> clustering_lifo; + static_assert(sizeof(clustering_lifo) == 0x9A0, "clustering_lifo is an invalid size"); + INSERT_PADDING_WORDS(0x11F); + }; + static_assert(sizeof(ClusteringSharedMemory) == 0xE20, + "ClusteringSharedMemory is an invalid size"); + + void OnControllerUpdate(Core::HID::ControllerTriggerType type); + void RemoveLowIntensityData(std::vector<u8>& data); + ClusteringData GetClusterProperties(std::vector<u8>& data, std::size_t x, std::size_t y); + ClusteringData GetPixelProperties(const std::vector<u8>& data, std::size_t x, + std::size_t y) const; + ClusteringData MergeCluster(const ClusteringData a, const ClusteringData b) const; + u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const; + void SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value); + + // Sets config parameters of the camera + void SetDefaultConfig(); + + ClusteringSharedMemory* shared_memory = nullptr; + ClusteringProcessorState next_state{}; + + ClusteringProcessorConfig current_config{}; + Core::IrSensor::DeviceFormat& device; + Core::HID::EmulatedController* npad_device; + int callback_key{}; +}; +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp new file mode 100644 index 000000000..98f0c579d --- /dev/null +++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp @@ -0,0 +1,150 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hle/service/hid/irsensor/image_transfer_processor.h" + +namespace Service::IRS { +ImageTransferProcessor::ImageTransferProcessor(Core::HID::HIDCore& hid_core_, + Core::IrSensor::DeviceFormat& device_format, + std::size_t npad_index) + : device{device_format} { + npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index); + + Core::HID::ControllerUpdateCallback engine_callback{ + .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); }, + .is_npad_service = true, + }; + callback_key = npad_device->SetCallback(engine_callback); + + device.mode = Core::IrSensor::IrSensorMode::ImageTransferProcessor; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; +} + +ImageTransferProcessor::~ImageTransferProcessor() { + npad_device->DeleteCallback(callback_key); +}; + +void ImageTransferProcessor::StartProcessor() { + is_active = true; + device.camera_status = Core::IrSensor::IrCameraStatus::Available; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready; + processor_state.sampling_number = 0; + processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; +} + +void ImageTransferProcessor::SuspendProcessor() {} + +void ImageTransferProcessor::StopProcessor() {} + +void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) { + if (type != Core::HID::ControllerTriggerType::IrSensor) { + return; + } + if (!is_transfer_memory_set) { + return; + } + + const auto camera_data = npad_device->GetCamera(); + + // This indicates how much ambient light is precent + processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; + processor_state.sampling_number = camera_data.sample; + + if (camera_data.format != current_config.origin_format) { + LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format, + current_config.origin_format); + memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); + return; + } + + if (current_config.origin_format > current_config.trimming_format) { + LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}", + current_config.origin_format, current_config.trimming_format); + memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); + return; + } + + std::vector<u8> window_data{}; + const auto origin_width = GetDataWidth(current_config.origin_format); + const auto origin_height = GetDataHeight(current_config.origin_format); + const auto trimming_width = GetDataWidth(current_config.trimming_format); + const auto trimming_height = GetDataHeight(current_config.trimming_format); + window_data.resize(GetDataSize(current_config.trimming_format)); + + if (trimming_width + current_config.trimming_start_x > origin_width || + trimming_height + current_config.trimming_start_y > origin_height) { + LOG_WARNING(Service_IRS, + "Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})", + current_config.trimming_start_x, current_config.trimming_start_y, + trimming_width, trimming_height, origin_width, origin_height); + memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); + return; + } + + for (std::size_t y = 0; y < trimming_height; y++) { + for (std::size_t x = 0; x < trimming_width; x++) { + const std::size_t window_index = (y * trimming_width) + x; + const std::size_t origin_index = + ((y + current_config.trimming_start_y) * origin_width) + x + + current_config.trimming_start_x; + window_data[window_index] = camera_data.data[origin_index]; + } + } + + memcpy(transfer_memory, window_data.data(), GetDataSize(current_config.trimming_format)); + + if (!IsProcessorActive()) { + StartProcessor(); + } +} + +void ImageTransferProcessor::SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config) { + current_config.camera_config.exposure_time = config.camera_config.exposure_time; + current_config.camera_config.gain = config.camera_config.gain; + current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; + current_config.camera_config.light_target = + static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); + current_config.origin_format = + static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format); + current_config.trimming_format = + static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format); + current_config.trimming_start_x = 0; + current_config.trimming_start_y = 0; + + npad_device->SetCameraFormat(current_config.origin_format); +} + +void ImageTransferProcessor::SetConfig( + Core::IrSensor::PackedImageTransferProcessorExConfig config) { + current_config.camera_config.exposure_time = config.camera_config.exposure_time; + current_config.camera_config.gain = config.camera_config.gain; + current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; + current_config.camera_config.light_target = + static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); + current_config.origin_format = + static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.origin_format); + current_config.trimming_format = + static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.trimming_format); + current_config.trimming_start_x = config.trimming_start_x; + current_config.trimming_start_y = config.trimming_start_y; + + npad_device->SetCameraFormat(current_config.origin_format); +} + +void ImageTransferProcessor::SetTransferMemoryPointer(u8* t_mem) { + is_transfer_memory_set = true; + transfer_memory = t_mem; +} + +Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState( + std::vector<u8>& data) const { + const auto size = GetDataSize(current_config.trimming_format); + data.resize(size); + memcpy(data.data(), transfer_memory, size); + return processor_state; +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.h b/src/core/hle/service/hid/irsensor/image_transfer_processor.h new file mode 100644 index 000000000..393df492d --- /dev/null +++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.h @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::IRS { +class ImageTransferProcessor final : public ProcessorBase { +public: + explicit ImageTransferProcessor(Core::HID::HIDCore& hid_core_, + Core::IrSensor::DeviceFormat& device_format, + std::size_t npad_index); + ~ImageTransferProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config); + void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config); + + // Transfer memory where the image data will be stored + void SetTransferMemoryPointer(u8* t_mem); + + Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const; + +private: + // This is nn::irsensor::ImageTransferProcessorConfig + struct ImageTransferProcessorConfig { + Core::IrSensor::CameraConfig camera_config; + Core::IrSensor::ImageTransferProcessorFormat format; + }; + static_assert(sizeof(ImageTransferProcessorConfig) == 0x20, + "ImageTransferProcessorConfig is an invalid size"); + + // This is nn::irsensor::ImageTransferProcessorExConfig + struct ImageTransferProcessorExConfig { + Core::IrSensor::CameraConfig camera_config; + Core::IrSensor::ImageTransferProcessorFormat origin_format; + Core::IrSensor::ImageTransferProcessorFormat trimming_format; + u16 trimming_start_x; + u16 trimming_start_y; + bool is_external_light_filter_enabled; + INSERT_PADDING_BYTES(3); + }; + static_assert(sizeof(ImageTransferProcessorExConfig) == 0x28, + "ImageTransferProcessorExConfig is an invalid size"); + + void OnControllerUpdate(Core::HID::ControllerTriggerType type); + + ImageTransferProcessorExConfig current_config{}; + Core::IrSensor::ImageTransferProcessorState processor_state{}; + Core::IrSensor::DeviceFormat& device; + Core::HID::EmulatedController* npad_device; + int callback_key{}; + + u8* transfer_memory = nullptr; + bool is_transfer_memory_set = false; +}; +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.cpp b/src/core/hle/service/hid/irsensor/ir_led_processor.cpp new file mode 100644 index 000000000..8e6dd99e4 --- /dev/null +++ b/src/core/hle/service/hid/irsensor/ir_led_processor.cpp @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/irsensor/ir_led_processor.h" + +namespace Service::IRS { +IrLedProcessor::IrLedProcessor(Core::IrSensor::DeviceFormat& device_format) + : device(device_format) { + device.mode = Core::IrSensor::IrSensorMode::IrLedProcessor; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; +} + +IrLedProcessor::~IrLedProcessor() = default; + +void IrLedProcessor::StartProcessor() {} + +void IrLedProcessor::SuspendProcessor() {} + +void IrLedProcessor::StopProcessor() {} + +void IrLedProcessor::SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config) { + current_config.light_target = + static_cast<Core::IrSensor::CameraLightTarget>(config.light_target); +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.h b/src/core/hle/service/hid/irsensor/ir_led_processor.h new file mode 100644 index 000000000..c3d8693c9 --- /dev/null +++ b/src/core/hle/service/hid/irsensor/ir_led_processor.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/bit_field.h" +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Service::IRS { +class IrLedProcessor final : public ProcessorBase { +public: + explicit IrLedProcessor(Core::IrSensor::DeviceFormat& device_format); + ~IrLedProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config); + +private: + // This is nn::irsensor::IrLedProcessorConfig + struct IrLedProcessorConfig { + Core::IrSensor::CameraLightTarget light_target; + }; + static_assert(sizeof(IrLedProcessorConfig) == 0x4, "IrLedProcessorConfig is an invalid size"); + + struct IrLedProcessorState { + s64 sampling_number; + u64 timestamp; + std::array<u8, 0x8> data; + }; + static_assert(sizeof(IrLedProcessorState) == 0x18, "IrLedProcessorState is an invalid size"); + + IrLedProcessorConfig current_config{}; + Core::IrSensor::DeviceFormat& device; +}; + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/moment_processor.cpp b/src/core/hle/service/hid/irsensor/moment_processor.cpp new file mode 100644 index 000000000..dbaca420a --- /dev/null +++ b/src/core/hle/service/hid/irsensor/moment_processor.cpp @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/irsensor/moment_processor.h" + +namespace Service::IRS { +MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format) + : device(device_format) { + device.mode = Core::IrSensor::IrSensorMode::MomentProcessor; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; +} + +MomentProcessor::~MomentProcessor() = default; + +void MomentProcessor::StartProcessor() {} + +void MomentProcessor::SuspendProcessor() {} + +void MomentProcessor::StopProcessor() {} + +void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) { + current_config.camera_config.exposure_time = config.camera_config.exposure_time; + current_config.camera_config.gain = config.camera_config.gain; + current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; + current_config.camera_config.light_target = + static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); + current_config.window_of_interest = config.window_of_interest; + current_config.preprocess = + static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess); + current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold; +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/moment_processor.h b/src/core/hle/service/hid/irsensor/moment_processor.h new file mode 100644 index 000000000..d4bd22e0f --- /dev/null +++ b/src/core/hle/service/hid/irsensor/moment_processor.h @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/bit_field.h" +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Service::IRS { +class MomentProcessor final : public ProcessorBase { +public: + explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format); + ~MomentProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config); + +private: + // This is nn::irsensor::MomentProcessorConfig + struct MomentProcessorConfig { + Core::IrSensor::CameraConfig camera_config; + Core::IrSensor::IrsRect window_of_interest; + Core::IrSensor::MomentProcessorPreprocess preprocess; + u32 preprocess_intensity_threshold; + }; + static_assert(sizeof(MomentProcessorConfig) == 0x28, + "MomentProcessorConfig is an invalid size"); + + // This is nn::irsensor::MomentStatistic + struct MomentStatistic { + f32 average_intensity; + Core::IrSensor::IrsCentroid centroid; + }; + static_assert(sizeof(MomentStatistic) == 0xC, "MomentStatistic is an invalid size"); + + // This is nn::irsensor::MomentProcessorState + struct MomentProcessorState { + s64 sampling_number; + u64 timestamp; + Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; + INSERT_PADDING_BYTES(4); + std::array<MomentStatistic, 0x30> stadistic; + }; + static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size"); + + MomentProcessorConfig current_config{}; + Core::IrSensor::DeviceFormat& device; +}; + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.cpp b/src/core/hle/service/hid/irsensor/pointing_processor.cpp new file mode 100644 index 000000000..929f177fc --- /dev/null +++ b/src/core/hle/service/hid/irsensor/pointing_processor.cpp @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/irsensor/pointing_processor.h" + +namespace Service::IRS { +PointingProcessor::PointingProcessor(Core::IrSensor::DeviceFormat& device_format) + : device(device_format) { + device.mode = Core::IrSensor::IrSensorMode::PointingProcessorMarker; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; +} + +PointingProcessor::~PointingProcessor() = default; + +void PointingProcessor::StartProcessor() {} + +void PointingProcessor::SuspendProcessor() {} + +void PointingProcessor::StopProcessor() {} + +void PointingProcessor::SetConfig(Core::IrSensor::PackedPointingProcessorConfig config) { + current_config.window_of_interest = config.window_of_interest; +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.h b/src/core/hle/service/hid/irsensor/pointing_processor.h new file mode 100644 index 000000000..cf4930794 --- /dev/null +++ b/src/core/hle/service/hid/irsensor/pointing_processor.h @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Service::IRS { +class PointingProcessor final : public ProcessorBase { +public: + explicit PointingProcessor(Core::IrSensor::DeviceFormat& device_format); + ~PointingProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedPointingProcessorConfig config); + +private: + // This is nn::irsensor::PointingProcessorConfig + struct PointingProcessorConfig { + Core::IrSensor::IrsRect window_of_interest; + }; + static_assert(sizeof(PointingProcessorConfig) == 0x8, + "PointingProcessorConfig is an invalid size"); + + struct PointingProcessorMarkerData { + u8 pointing_status; + INSERT_PADDING_BYTES(3); + u32 unknown; + float unkown_float1; + float position_x; + float position_y; + float unkown_float2; + Core::IrSensor::IrsRect window_of_interest; + }; + static_assert(sizeof(PointingProcessorMarkerData) == 0x20, + "PointingProcessorMarkerData is an invalid size"); + + struct PointingProcessorMarkerState { + s64 sampling_number; + u64 timestamp; + std::array<PointingProcessorMarkerData, 0x3> data; + }; + static_assert(sizeof(PointingProcessorMarkerState) == 0x70, + "PointingProcessorMarkerState is an invalid size"); + + PointingProcessorConfig current_config{}; + Core::IrSensor::DeviceFormat& device; +}; + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/processor_base.cpp b/src/core/hle/service/hid/irsensor/processor_base.cpp new file mode 100644 index 000000000..4d43ca17a --- /dev/null +++ b/src/core/hle/service/hid/irsensor/processor_base.cpp @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Service::IRS { + +ProcessorBase::ProcessorBase() {} +ProcessorBase::~ProcessorBase() = default; + +bool ProcessorBase::IsProcessorActive() const { + return is_active; +} + +std::size_t ProcessorBase::GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const { + switch (format) { + case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: + return 320 * 240; + case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: + return 160 * 120; + case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: + return 80 * 60; + case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: + return 40 * 30; + case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: + return 20 * 15; + default: + return 0; + } +} + +std::size_t ProcessorBase::GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const { + switch (format) { + case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: + return 320; + case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: + return 160; + case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: + return 80; + case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: + return 40; + case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: + return 20; + default: + return 0; + } +} + +std::size_t ProcessorBase::GetDataHeight( + Core::IrSensor::ImageTransferProcessorFormat format) const { + switch (format) { + case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: + return 240; + case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: + return 120; + case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: + return 60; + case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: + return 30; + case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: + return 15; + default: + return 0; + } +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/processor_base.h b/src/core/hle/service/hid/irsensor/processor_base.h new file mode 100644 index 000000000..bc0d2977b --- /dev/null +++ b/src/core/hle/service/hid/irsensor/processor_base.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hid/irs_types.h" + +namespace Service::IRS { +class ProcessorBase { +public: + explicit ProcessorBase(); + virtual ~ProcessorBase(); + + virtual void StartProcessor() = 0; + virtual void SuspendProcessor() = 0; + virtual void StopProcessor() = 0; + + bool IsProcessorActive() const; + +protected: + /// Returns the number of bytes the image uses + std::size_t GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const; + + /// Returns the width of the image + std::size_t GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const; + + /// Returns the height of the image + std::size_t GetDataHeight(Core::IrSensor::ImageTransferProcessorFormat format) const; + + bool is_active{false}; +}; +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp b/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp new file mode 100644 index 000000000..e691c840a --- /dev/null +++ b/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/irsensor/tera_plugin_processor.h" + +namespace Service::IRS { +TeraPluginProcessor::TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format) + : device(device_format) { + device.mode = Core::IrSensor::IrSensorMode::TeraPluginProcessor; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; +} + +TeraPluginProcessor::~TeraPluginProcessor() = default; + +void TeraPluginProcessor::StartProcessor() {} + +void TeraPluginProcessor::SuspendProcessor() {} + +void TeraPluginProcessor::StopProcessor() {} + +void TeraPluginProcessor::SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config) { + current_config.mode = config.mode; + current_config.unknown_1 = config.unknown_1; + current_config.unknown_2 = config.unknown_2; + current_config.unknown_3 = config.unknown_3; +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.h b/src/core/hle/service/hid/irsensor/tera_plugin_processor.h new file mode 100644 index 000000000..bbea7ed0b --- /dev/null +++ b/src/core/hle/service/hid/irsensor/tera_plugin_processor.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/bit_field.h" +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Service::IRS { +class TeraPluginProcessor final : public ProcessorBase { +public: + explicit TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format); + ~TeraPluginProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config); + +private: + // This is nn::irsensor::TeraPluginProcessorConfig + struct TeraPluginProcessorConfig { + u8 mode; + u8 unknown_1; + u8 unknown_2; + u8 unknown_3; + }; + static_assert(sizeof(TeraPluginProcessorConfig) == 0x4, + "TeraPluginProcessorConfig is an invalid size"); + + struct TeraPluginProcessorState { + s64 sampling_number; + u64 timestamp; + Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; + std::array<u8, 0x12c> data; + }; + static_assert(sizeof(TeraPluginProcessorState) == 0x140, + "TeraPluginProcessorState is an invalid size"); + + TeraPluginProcessorConfig current_config{}; + Core::IrSensor::DeviceFormat& device; +}; + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/ring_lifo.h b/src/core/hle/service/hid/ring_lifo.h index 44c20d967..65eb7ea02 100644 --- a/src/core/hle/service/hid/ring_lifo.h +++ b/src/core/hle/service/hid/ring_lifo.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/hid/xcd.cpp b/src/core/hle/service/hid/xcd.cpp index b1efa3d05..75cc266ea 100644 --- a/src/core/hle/service/hid/xcd.cpp +++ b/src/core/hle/service/hid/xcd.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/hid/xcd.h" diff --git a/src/core/hle/service/hid/xcd.h b/src/core/hle/service/hid/xcd.h index 54932c228..fe0b91b91 100644 --- a/src/core/hle/service/hid/xcd.h +++ b/src/core/hle/service/hid/xcd.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp new file mode 100644 index 000000000..8f2920c51 --- /dev/null +++ b/src/core/hle/service/jit/jit.cpp @@ -0,0 +1,404 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/arm/symbols.h" +#include "core/core.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_code_memory.h" +#include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/result.h" +#include "core/hle/service/jit/jit.h" +#include "core/hle/service/jit/jit_context.h" +#include "core/hle/service/service.h" +#include "core/memory.h" + +namespace Service::JIT { + +struct CodeRange { + u64 offset; + u64 size; +}; + +class IJitEnvironment final : public ServiceFramework<IJitEnvironment> { +public: + explicit IJitEnvironment(Core::System& system_, Kernel::KProcess& process_, CodeRange user_rx, + CodeRange user_ro) + : ServiceFramework{system_, "IJitEnvironment", ServiceThreadType::CreateNew}, + process{&process_}, context{system_.Memory()} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IJitEnvironment::GenerateCode, "GenerateCode"}, + {1, &IJitEnvironment::Control, "Control"}, + {1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"}, + {1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"}, + }; + // clang-format on + + RegisterHandlers(functions); + + // Identity map user code range into sysmodule context + configuration.user_ro_memory = user_ro; + configuration.user_rx_memory = user_rx; + configuration.sys_ro_memory = user_ro; + configuration.sys_rx_memory = user_rx; + } + + void GenerateCode(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_JIT, "called"); + + struct InputParameters { + u32 data_size; + u64 command; + std::array<CodeRange, 2> ranges; + Struct32 data; + }; + + struct OutputParameters { + s32 return_value; + std::array<CodeRange, 2> ranges; + }; + + IPC::RequestParser rp{ctx}; + const auto parameters{rp.PopRaw<InputParameters>()}; + + // Optional input/output buffers + std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()}; + std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); + + // Function call prototype: + // void GenerateCode(s32* ret, CodeRange* c0_out, CodeRange* c1_out, JITConfiguration* cfg, + // u64 cmd, u8* input_buf, size_t input_size, CodeRange* c0_in, + // CodeRange* c1_in, Struct32* data, size_t data_size, u8* output_buf, + // size_t output_size); + // + // The command argument is used to control the behavior of the plugin during code + // generation. The configuration allows the plugin to access the output code ranges, and the + // other arguments are used to transfer state between the game and the plugin. + + const VAddr ret_ptr{context.AddHeap(0u)}; + const VAddr c0_in_ptr{context.AddHeap(parameters.ranges[0])}; + const VAddr c1_in_ptr{context.AddHeap(parameters.ranges[1])}; + const VAddr c0_out_ptr{context.AddHeap(ClearSize(parameters.ranges[0]))}; + const VAddr c1_out_ptr{context.AddHeap(ClearSize(parameters.ranges[1]))}; + + const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())}; + const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())}; + const VAddr data_ptr{context.AddHeap(parameters.data)}; + const VAddr configuration_ptr{context.AddHeap(configuration)}; + + // The callback does not directly return a value, it only writes to the output pointer + context.CallFunction(callbacks.GenerateCode, ret_ptr, c0_out_ptr, c1_out_ptr, + configuration_ptr, parameters.command, input_ptr, input_buffer.size(), + c0_in_ptr, c1_in_ptr, data_ptr, parameters.data_size, output_ptr, + output_buffer.size()); + + const s32 return_value{context.GetHeap<s32>(ret_ptr)}; + + if (return_value == 0) { + // The callback has written to the output executable code range, + // requiring an instruction cache invalidation + system.InvalidateCpuInstructionCacheRange(configuration.user_rx_memory.offset, + configuration.user_rx_memory.size); + + // Write back to the IPC output buffer, if provided + if (ctx.CanWriteBuffer()) { + context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size()); + ctx.WriteBuffer(output_buffer.data(), output_buffer.size()); + } + + const OutputParameters out{ + .return_value = return_value, + .ranges = + { + context.GetHeap<CodeRange>(c0_out_ptr), + context.GetHeap<CodeRange>(c1_out_ptr), + }, + }; + + IPC::ResponseBuilder rb{ctx, 8}; + rb.Push(ResultSuccess); + rb.PushRaw(out); + } else { + LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + } + }; + + void Control(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_JIT, "called"); + + IPC::RequestParser rp{ctx}; + const auto command{rp.PopRaw<u64>()}; + + // Optional input/output buffers + std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()}; + std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); + + // Function call prototype: + // u64 Control(s32* ret, JITConfiguration* cfg, u64 cmd, u8* input_buf, size_t input_size, + // u8* output_buf, size_t output_size); + // + // This function is used to set up the state of the plugin before code generation, generally + // passing objects like pointers to VM state from the game. It is usually called once. + + const VAddr ret_ptr{context.AddHeap(0u)}; + const VAddr configuration_ptr{context.AddHeap(configuration)}; + const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())}; + const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())}; + + const u64 wrapper_value{context.CallFunction(callbacks.Control, ret_ptr, configuration_ptr, + command, input_ptr, input_buffer.size(), + output_ptr, output_buffer.size())}; + + const s32 return_value{context.GetHeap<s32>(ret_ptr)}; + + if (wrapper_value == 0 && return_value == 0) { + // Write back to the IPC output buffer, if provided + if (ctx.CanWriteBuffer()) { + context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size()); + ctx.WriteBuffer(output_buffer.data(), output_buffer.size()); + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(return_value); + } else { + LOG_WARNING(Service_JIT, "plugin Control callback failed"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + } + } + + void LoadPlugin(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_JIT, "called"); + + IPC::RequestParser rp{ctx}; + const auto tmem_size{rp.PopRaw<u64>()}; + const auto tmem_handle{ctx.GetCopyHandle(0)}; + const auto nro_plugin{ctx.ReadBuffer(1)}; + + if (tmem_size == 0) { + LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + auto tmem{process->GetHandleTable().GetObject<Kernel::KTransferMemory>(tmem_handle)}; + if (tmem.IsNull()) { + LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + // Set up the configuration with the required TransferMemory address + configuration.transfer_memory.offset = tmem->GetSourceAddress(); + configuration.transfer_memory.size = tmem_size; + + // Gather up all the callbacks from the loaded plugin + auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)}; + const auto GetSymbol{[&](const std::string& name) { return symbols[name].first; }}; + + callbacks.rtld_fini = GetSymbol("_fini"); + callbacks.rtld_init = GetSymbol("_init"); + callbacks.Control = GetSymbol("nnjitpluginControl"); + callbacks.ResolveBasicSymbols = GetSymbol("nnjitpluginResolveBasicSymbols"); + callbacks.SetupDiagnostics = GetSymbol("nnjitpluginSetupDiagnostics"); + callbacks.Configure = GetSymbol("nnjitpluginConfigure"); + callbacks.GenerateCode = GetSymbol("nnjitpluginGenerateCode"); + callbacks.GetVersion = GetSymbol("nnjitpluginGetVersion"); + callbacks.OnPrepared = GetSymbol("nnjitpluginOnPrepared"); + callbacks.Keeper = GetSymbol("nnjitpluginKeeper"); + + if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 || + callbacks.OnPrepared == 0) { + LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + if (!context.LoadNRO(nro_plugin)) { + LOG_ERROR(Service_JIT, "failed to load plugin"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + context.MapProcessMemory(configuration.sys_ro_memory.offset, + configuration.sys_ro_memory.size); + context.MapProcessMemory(configuration.sys_rx_memory.offset, + configuration.sys_rx_memory.size); + context.MapProcessMemory(configuration.transfer_memory.offset, + configuration.transfer_memory.size); + + // Run ELF constructors, if needed + if (callbacks.rtld_init != 0) { + context.CallFunction(callbacks.rtld_init); + } + + // Function prototype: + // u64 GetVersion(); + const auto version{context.CallFunction(callbacks.GetVersion)}; + if (version != 1) { + LOG_ERROR(Service_JIT, "unknown plugin version {}", version); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + // Function prototype: + // void ResolveBasicSymbols(void (*resolver)(const char* name)); + const auto resolve{context.GetHelper("_resolve")}; + if (callbacks.ResolveBasicSymbols != 0) { + context.CallFunction(callbacks.ResolveBasicSymbols, resolve); + } + + // Function prototype: + // void SetupDiagnostics(u32 enabled, void (**resolver)(const char* name)); + const auto resolve_ptr{context.AddHeap(resolve)}; + if (callbacks.SetupDiagnostics != 0) { + context.CallFunction(callbacks.SetupDiagnostics, 0u, resolve_ptr); + } + + // Function prototype: + // void Configure(u32* memory_flags); + context.CallFunction(callbacks.Configure, 0ull); + + // Function prototype: + // void OnPrepared(JITConfiguration* cfg); + const auto configuration_ptr{context.AddHeap(configuration)}; + context.CallFunction(callbacks.OnPrepared, configuration_ptr); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void GetCodeAddress(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_JIT, "called"); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.Push(configuration.user_rx_memory.offset); + rb.Push(configuration.user_ro_memory.offset); + } + +private: + using Struct32 = std::array<u8, 32>; + + struct GuestCallbacks { + VAddr rtld_fini; + VAddr rtld_init; + VAddr Control; + VAddr ResolveBasicSymbols; + VAddr SetupDiagnostics; + VAddr Configure; + VAddr GenerateCode; + VAddr GetVersion; + VAddr Keeper; + VAddr OnPrepared; + }; + + struct JITConfiguration { + CodeRange user_rx_memory; + CodeRange user_ro_memory; + CodeRange transfer_memory; + CodeRange sys_rx_memory; + CodeRange sys_ro_memory; + }; + + static CodeRange ClearSize(CodeRange in) { + in.size = 0; + return in; + } + + Kernel::KScopedAutoObject<Kernel::KProcess> process; + GuestCallbacks callbacks; + JITConfiguration configuration; + JITContext context; +}; + +class JITU final : public ServiceFramework<JITU> { +public: + explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + + void CreateJitEnvironment(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_JIT, "called"); + + struct Parameters { + u64 rx_size; + u64 ro_size; + }; + + IPC::RequestParser rp{ctx}; + const auto parameters{rp.PopRaw<Parameters>()}; + const auto process_handle{ctx.GetCopyHandle(0)}; + const auto rx_mem_handle{ctx.GetCopyHandle(1)}; + const auto ro_mem_handle{ctx.GetCopyHandle(2)}; + + if (parameters.rx_size == 0 || parameters.ro_size == 0) { + LOG_ERROR(Service_JIT, "attempted to init with empty code regions"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + // Fetch using the handle table for the current process here, + // since we are not multiprocess yet. + const auto& handle_table{system.CurrentProcess()->GetHandleTable()}; + + auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)}; + if (process.IsNull()) { + LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + auto rx_mem{handle_table.GetObject<Kernel::KCodeMemory>(rx_mem_handle)}; + if (rx_mem.IsNull()) { + LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + auto ro_mem{handle_table.GetObject<Kernel::KCodeMemory>(ro_mem_handle)}; + if (ro_mem.IsNull()) { + LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + const CodeRange user_rx{ + .offset = rx_mem->GetSourceAddress(), + .size = parameters.rx_size, + }; + + const CodeRange user_ro{ + .offset = ro_mem->GetSourceAddress(), + .size = parameters.ro_size, + }; + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IJitEnvironment>(system, *process, user_rx, user_ro); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { + std::make_shared<JITU>(system)->InstallAsService(sm); +} + +} // namespace Service::JIT diff --git a/src/core/hle/service/jit/jit.h b/src/core/hle/service/jit/jit.h new file mode 100644 index 000000000..af0f5b4f3 --- /dev/null +++ b/src/core/hle/service/jit/jit.h @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace Core { +class System; +} + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::JIT { + +/// Registers all JIT services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); + +} // namespace Service::JIT diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp new file mode 100644 index 000000000..4ed3f02e2 --- /dev/null +++ b/src/core/hle/service/jit/jit_context.cpp @@ -0,0 +1,426 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <array> +#include <map> +#include <span> +#include <boost/icl/interval_set.hpp> +#include <dynarmic/interface/A64/a64.h> +#include <dynarmic/interface/A64/config.h> + +#include "common/alignment.h" +#include "common/common_funcs.h" +#include "common/div_ceil.h" +#include "common/elf.h" +#include "common/logging/log.h" +#include "core/hle/service/jit/jit_context.h" +#include "core/memory.h" + +using namespace Common::ELF; + +namespace Service::JIT { + +constexpr std::array<u8, 8> SVC0_ARM64 = { + 0x01, 0x00, 0x00, 0xd4, // svc #0 + 0xc0, 0x03, 0x5f, 0xd6, // ret +}; + +constexpr std::array HELPER_FUNCTIONS{ + "_stop", "_resolve", "_panic", "memcpy", "memmove", "memset", +}; + +constexpr size_t STACK_ALIGN = 16; + +class JITContextImpl; + +using IntervalSet = boost::icl::interval_set<VAddr>::type; +using IntervalType = boost::icl::interval_set<VAddr>::interval_type; + +class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { +public: + explicit DynarmicCallbacks64(Core::Memory::Memory& memory_, std::vector<u8>& local_memory_, + IntervalSet& mapped_ranges_, JITContextImpl& parent_) + : memory{memory_}, local_memory{local_memory_}, + mapped_ranges{mapped_ranges_}, parent{parent_} {} + + u8 MemoryRead8(u64 vaddr) override { + return ReadMemory<u8>(vaddr); + } + u16 MemoryRead16(u64 vaddr) override { + return ReadMemory<u16>(vaddr); + } + u32 MemoryRead32(u64 vaddr) override { + return ReadMemory<u32>(vaddr); + } + u64 MemoryRead64(u64 vaddr) override { + return ReadMemory<u64>(vaddr); + } + u128 MemoryRead128(u64 vaddr) override { + return ReadMemory<u128>(vaddr); + } + std::string MemoryReadCString(u64 vaddr) { + std::string result; + u8 next; + + while ((next = MemoryRead8(vaddr++)) != 0) { + result += next; + } + + return result; + } + + void MemoryWrite8(u64 vaddr, u8 value) override { + WriteMemory<u8>(vaddr, value); + } + void MemoryWrite16(u64 vaddr, u16 value) override { + WriteMemory<u16>(vaddr, value); + } + void MemoryWrite32(u64 vaddr, u32 value) override { + WriteMemory<u32>(vaddr, value); + } + void MemoryWrite64(u64 vaddr, u64 value) override { + WriteMemory<u64>(vaddr, value); + } + void MemoryWrite128(u64 vaddr, u128 value) override { + WriteMemory<u128>(vaddr, value); + } + + bool MemoryWriteExclusive8(u64 vaddr, u8 value, u8) override { + return WriteMemory<u8>(vaddr, value); + } + bool MemoryWriteExclusive16(u64 vaddr, u16 value, u16) override { + return WriteMemory<u16>(vaddr, value); + } + bool MemoryWriteExclusive32(u64 vaddr, u32 value, u32) override { + return WriteMemory<u32>(vaddr, value); + } + bool MemoryWriteExclusive64(u64 vaddr, u64 value, u64) override { + return WriteMemory<u64>(vaddr, value); + } + bool MemoryWriteExclusive128(u64 vaddr, u128 value, u128) override { + return WriteMemory<u128>(vaddr, value); + } + + void CallSVC(u32 swi) override; + void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override; + void InterpreterFallback(u64 pc, size_t num_instructions) override; + + void AddTicks(u64 ticks) override {} + u64 GetTicksRemaining() override { + return std::numeric_limits<u32>::max(); + } + u64 GetCNTPCT() override { + return 0; + } + + template <class T> + T ReadMemory(u64 vaddr) { + T ret{}; + if (boost::icl::contains(mapped_ranges, vaddr)) { + memory.ReadBlock(vaddr, &ret, sizeof(T)); + } else if (vaddr + sizeof(T) > local_memory.size()) { + LOG_CRITICAL(Service_JIT, "plugin: unmapped read @ 0x{:016x}", vaddr); + } else { + std::memcpy(&ret, local_memory.data() + vaddr, sizeof(T)); + } + return ret; + } + + template <class T> + bool WriteMemory(u64 vaddr, const T value) { + if (boost::icl::contains(mapped_ranges, vaddr)) { + memory.WriteBlock(vaddr, &value, sizeof(T)); + } else if (vaddr + sizeof(T) > local_memory.size()) { + LOG_CRITICAL(Service_JIT, "plugin: unmapped write @ 0x{:016x}", vaddr); + } else { + std::memcpy(local_memory.data() + vaddr, &value, sizeof(T)); + } + return true; + } + +private: + Core::Memory::Memory& memory; + std::vector<u8>& local_memory; + IntervalSet& mapped_ranges; + JITContextImpl& parent; +}; + +class JITContextImpl { +public: + explicit JITContextImpl(Core::Memory::Memory& memory_) : memory{memory_} { + callbacks = + std::make_unique<DynarmicCallbacks64>(memory, local_memory, mapped_ranges, *this); + user_config.callbacks = callbacks.get(); + jit = std::make_unique<Dynarmic::A64::Jit>(user_config); + } + + bool LoadNRO(std::span<const u8> data) { + local_memory.clear(); + local_memory.insert(local_memory.end(), data.begin(), data.end()); + + if (FixupRelocations()) { + InsertHelperFunctions(); + InsertStack(); + return true; + } + + return false; + } + + bool FixupRelocations() { + // The loaded NRO file has ELF relocations that must be processed before it can run. + // Normally this would be processed by RTLD, but in HLE context, we don't have + // the linker available, so we have to do it ourselves. + + const VAddr mod_offset{callbacks->MemoryRead32(4)}; + if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) { + return false; + } + + // For more info about dynamic entries, see the ELF ABI specification: + // https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html + // https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html + VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)}; + VAddr rela_dyn = 0; + size_t num_rela = 0; + while (true) { + const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)}; + dynamic_offset += sizeof(Elf64_Dyn); + + if (!dyn.d_tag) { + break; + } + if (dyn.d_tag == ElfDtRela) { + rela_dyn = dyn.d_un.d_ptr; + } + if (dyn.d_tag == ElfDtRelasz) { + num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela); + } + } + + for (size_t i = 0; i < num_rela; i++) { + const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))}; + if (Elf64RelType(rela.r_info) != ElfAArch64Relative) { + continue; + } + const VAddr contents{callbacks->MemoryRead64(rela.r_offset)}; + callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend); + } + + return true; + } + + void InsertHelperFunctions() { + for (const auto& name : HELPER_FUNCTIONS) { + helpers[name] = local_memory.size(); + local_memory.insert(local_memory.end(), SVC0_ARM64.begin(), SVC0_ARM64.end()); + } + } + + void InsertStack() { + // Allocate enough space to avoid any reasonable risk of + // overflowing the stack during plugin execution + const u64 pad_amount{Common::AlignUp(local_memory.size(), STACK_ALIGN) - + local_memory.size()}; + local_memory.insert(local_memory.end(), 0x10000 + pad_amount, 0); + top_of_stack = local_memory.size(); + heap_pointer = top_of_stack; + } + + void MapProcessMemory(VAddr dest_address, std::size_t size) { + mapped_ranges.add(IntervalType{dest_address, dest_address + size}); + } + + void PushArgument(const void* data, size_t size) { + const size_t num_words = Common::DivCeil(size, sizeof(u64)); + const size_t current_pos = argument_stack.size(); + argument_stack.insert(argument_stack.end(), num_words, 0); + std::memcpy(argument_stack.data() + current_pos, data, size); + } + + void SetupArguments() { + // The first 8 integer registers are used for the first 8 integer + // arguments. Floating-point arguments are not handled at this time. + // + // If a function takes more than 8 arguments, then stack space is reserved + // for the remaining arguments, and the remaining arguments are inserted in + // ascending memory order, each argument aligned to an 8-byte boundary. The + // stack pointer must remain aligned to 16 bytes. + // + // For more info, see the AArch64 ABI PCS: + // https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst + + for (size_t i = 0; i < 8 && i < argument_stack.size(); i++) { + jit->SetRegister(i, argument_stack[i]); + } + + if (argument_stack.size() > 8) { + const VAddr new_sp = Common::AlignDown( + top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN); + for (size_t i = 8; i < argument_stack.size(); i++) { + callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]); + } + jit->SetSP(new_sp); + } + + // Reset the call state for the next invocation + argument_stack.clear(); + heap_pointer = top_of_stack; + } + + u64 CallFunction(VAddr func) { + jit->SetRegister(30, helpers["_stop"]); + jit->SetSP(top_of_stack); + SetupArguments(); + + jit->SetPC(func); + jit->Run(); + return jit->GetRegister(0); + } + + VAddr GetHelper(const std::string& name) { + return helpers[name]; + } + + VAddr AddHeap(const void* data, size_t size) { + // Require all heap data types to have the same alignment as the + // stack pointer, for compatibility + const size_t num_bytes{Common::AlignUp(size, STACK_ALIGN)}; + + // Make additional memory space if required + if (heap_pointer + num_bytes > local_memory.size()) { + local_memory.insert(local_memory.end(), + (heap_pointer + num_bytes) - local_memory.size(), 0); + } + + const VAddr location{heap_pointer}; + std::memcpy(local_memory.data() + location, data, size); + heap_pointer += num_bytes; + return location; + } + + void GetHeap(VAddr location, void* data, size_t size) { + std::memcpy(data, local_memory.data() + location, size); + } + + std::unique_ptr<DynarmicCallbacks64> callbacks; + std::vector<u8> local_memory; + std::vector<u64> argument_stack; + IntervalSet mapped_ranges; + Dynarmic::A64::UserConfig user_config; + std::unique_ptr<Dynarmic::A64::Jit> jit; + std::map<std::string, VAddr, std::less<>> helpers; + Core::Memory::Memory& memory; + VAddr top_of_stack; + VAddr heap_pointer; +}; + +void DynarmicCallbacks64::CallSVC(u32 swi) { + // Service calls are used to implement helper functionality. + // + // The most important of these is the _stop helper, which transfers control + // from the plugin back to HLE context to return a value. However, a few more + // are also implemented to reduce the need for direct ARM implementations of + // basic functionality, like memory operations. + // + // When we receive a helper request, the swi number will be zero, and the call + // will have originated from an address we know is a helper function. Otherwise, + // the plugin may be trying to issue a service call, which we shouldn't handle. + + if (swi != 0) { + LOG_CRITICAL(Service_JIT, "plugin issued unknown service call {}", swi); + parent.jit->HaltExecution(); + return; + } + + u64 pc{parent.jit->GetPC() - 4}; + auto& helpers{parent.helpers}; + + if (pc == helpers["memcpy"] || pc == helpers["memmove"]) { + const VAddr dest{parent.jit->GetRegister(0)}; + const VAddr src{parent.jit->GetRegister(1)}; + const size_t n{parent.jit->GetRegister(2)}; + + if (dest < src) { + for (size_t i = 0; i < n; i++) { + MemoryWrite8(dest + i, MemoryRead8(src + i)); + } + } else { + for (size_t i = n; i > 0; i--) { + MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1)); + } + } + } else if (pc == helpers["memset"]) { + const VAddr dest{parent.jit->GetRegister(0)}; + const u64 c{parent.jit->GetRegister(1)}; + const size_t n{parent.jit->GetRegister(2)}; + + for (size_t i = 0; i < n; i++) { + MemoryWrite8(dest + i, static_cast<u8>(c)); + } + } else if (pc == helpers["_resolve"]) { + // X0 contains a char* for a symbol to resolve + const auto name{MemoryReadCString(parent.jit->GetRegister(0))}; + const auto helper{helpers[name]}; + + if (helper != 0) { + parent.jit->SetRegister(0, helper); + } else { + LOG_WARNING(Service_JIT, "plugin requested unknown function {}", name); + parent.jit->SetRegister(0, helpers["_panic"]); + } + } else if (pc == helpers["_stop"]) { + parent.jit->HaltExecution(); + } else if (pc == helpers["_panic"]) { + LOG_CRITICAL(Service_JIT, "plugin panicked!"); + parent.jit->HaltExecution(); + } else { + LOG_CRITICAL(Service_JIT, "plugin issued syscall at unknown address 0x{:x}", pc); + parent.jit->HaltExecution(); + } +} + +void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) { + LOG_CRITICAL(Service_JIT, "Illegal operation PC @ {:08x}", pc); + parent.jit->HaltExecution(); +} + +void DynarmicCallbacks64::InterpreterFallback(u64 pc, size_t num_instructions) { + LOG_CRITICAL(Service_JIT, "Unimplemented instruction PC @ {:08x}", pc); + parent.jit->HaltExecution(); +} + +JITContext::JITContext(Core::Memory::Memory& memory) + : impl{std::make_unique<JITContextImpl>(memory)} {} + +JITContext::~JITContext() {} + +bool JITContext::LoadNRO(std::span<const u8> data) { + return impl->LoadNRO(data); +} + +void JITContext::MapProcessMemory(VAddr dest_address, std::size_t size) { + impl->MapProcessMemory(dest_address, size); +} + +u64 JITContext::CallFunction(VAddr func) { + return impl->CallFunction(func); +} + +void JITContext::PushArgument(const void* data, size_t size) { + impl->PushArgument(data, size); +} + +VAddr JITContext::GetHelper(const std::string& name) { + return impl->GetHelper(name); +} + +VAddr JITContext::AddHeap(const void* data, size_t size) { + return impl->AddHeap(data, size); +} + +void JITContext::GetHeap(VAddr location, void* data, size_t size) { + impl->GetHeap(location, data, size); +} + +} // namespace Service::JIT diff --git a/src/core/hle/service/jit/jit_context.h b/src/core/hle/service/jit/jit_context.h new file mode 100644 index 000000000..f17fc5e24 --- /dev/null +++ b/src/core/hle/service/jit/jit_context.h @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> +#include <span> +#include <string> + +#include "common/common_types.h" + +namespace Core::Memory { +class Memory; +} + +namespace Service::JIT { + +class JITContextImpl; + +class JITContext { +public: + explicit JITContext(Core::Memory::Memory& memory); + ~JITContext(); + + [[nodiscard]] bool LoadNRO(std::span<const u8> data); + void MapProcessMemory(VAddr dest_address, std::size_t size); + + template <typename T, typename... Ts> + u64 CallFunction(VAddr func, T argument, Ts... rest) { + static_assert(std::is_trivially_copyable_v<T>); + static_assert(!std::is_floating_point_v<T>); + PushArgument(&argument, sizeof(argument)); + + if constexpr (sizeof...(rest) > 0) { + return CallFunction(func, rest...); + } else { + return CallFunction(func); + } + } + + u64 CallFunction(VAddr func); + VAddr GetHelper(const std::string& name); + + template <typename T> + VAddr AddHeap(T argument) { + return AddHeap(&argument, sizeof(argument)); + } + VAddr AddHeap(const void* data, size_t size); + + template <typename T> + T GetHeap(VAddr location) { + static_assert(std::is_trivially_copyable_v<T>); + T result; + GetHeap(location, &result, sizeof(result)); + return result; + } + void GetHeap(VAddr location, void* data, size_t size); + +private: + std::unique_ptr<JITContextImpl> impl; + + void PushArgument(const void* data, size_t size); +}; + +} // namespace Service::JIT diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp index 62f4cdfb2..3e317367b 100644 --- a/src/core/hle/service/kernel_helpers.cpp +++ b/src/core/hle/service/kernel_helpers.cpp @@ -1,9 +1,10 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" +#include "core/core_timing.h" #include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_memory_manager.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_resource_limit.h" @@ -15,9 +16,11 @@ namespace Service::KernelHelpers { ServiceContext::ServiceContext(Core::System& system_, std::string name_) : kernel(system_.Kernel()) { + // Create the process. process = Kernel::KProcess::Create(kernel); ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_), - Kernel::KProcess::ProcessType::Userland) + Kernel::KProcess::ProcessType::KernelInternal, + kernel.GetSystemResourceLimit()) .IsSuccess()); } @@ -43,7 +46,7 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { } // Initialize the event. - event->Initialize(std::move(name)); + event->Initialize(std::move(name), process); // Commit the thread reservation. event_reservation.Commit(); diff --git a/src/core/hle/service/kernel_helpers.h b/src/core/hle/service/kernel_helpers.h index 4f3e95f67..6415838e5 100644 --- a/src/core/hle/service/kernel_helpers.h +++ b/src/core/hle/service/kernel_helpers.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp index 5c8ae029c..c8415e0bf 100644 --- a/src/core/hle/service/lbl/lbl.cpp +++ b/src/core/hle/service/lbl/lbl.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cmath> #include <memory> diff --git a/src/core/hle/service/lbl/lbl.h b/src/core/hle/service/lbl/lbl.h index 9c2021026..6484105c2 100644 --- a/src/core/hle/service/lbl/lbl.h +++ b/src/core/hle/service/lbl/lbl.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/ldn/errors.h b/src/core/hle/service/ldn/errors.h deleted file mode 100644 index a718c5c66..000000000 --- a/src/core/hle/service/ldn/errors.h +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "core/hle/result.h" - -namespace Service::LDN { - -constexpr ResultCode ERROR_DISABLED{ErrorModule::LDN, 22}; - -} // namespace Service::LDN diff --git a/src/core/hle/service/ldn/lan_discovery.cpp b/src/core/hle/service/ldn/lan_discovery.cpp new file mode 100644 index 000000000..8f3c04550 --- /dev/null +++ b/src/core/hle/service/ldn/lan_discovery.cpp @@ -0,0 +1,633 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/ldn/lan_discovery.h" +#include "core/internal_network/network.h" +#include "core/internal_network/network_interface.h" + +namespace Service::LDN { + +LanStation::LanStation(s8 node_id_, LANDiscovery* discovery_) + : node_info(nullptr), status(NodeStatus::Disconnected), node_id(node_id_), + discovery(discovery_) {} + +LanStation::~LanStation() = default; + +NodeStatus LanStation::GetStatus() const { + return status; +} + +void LanStation::OnClose() { + LOG_INFO(Service_LDN, "OnClose {}", node_id); + Reset(); + discovery->UpdateNodes(); +} + +void LanStation::Reset() { + status = NodeStatus::Disconnected; +}; + +void LanStation::OverrideInfo() { + bool connected = GetStatus() == NodeStatus::Connected; + node_info->node_id = node_id; + node_info->is_connected = connected ? 1 : 0; +} + +LANDiscovery::LANDiscovery(Network::RoomNetwork& room_network_) + : stations({{{1, this}, {2, this}, {3, this}, {4, this}, {5, this}, {6, this}, {7, this}}}), + room_network{room_network_} {} + +LANDiscovery::~LANDiscovery() { + if (inited) { + Result rc = Finalize(); + LOG_INFO(Service_LDN, "Finalize: {}", rc.raw); + } +} + +void LANDiscovery::InitNetworkInfo() { + network_info.common.bssid = GetFakeMac(); + network_info.common.channel = WifiChannel::Wifi24_6; + network_info.common.link_level = LinkLevel::Good; + network_info.common.network_type = PackedNetworkType::Ldn; + network_info.common.ssid = fake_ssid; + + auto& nodes = network_info.ldn.nodes; + for (std::size_t i = 0; i < NodeCountMax; i++) { + nodes[i].node_id = static_cast<s8>(i); + nodes[i].is_connected = 0; + } +} + +void LANDiscovery::InitNodeStateChange() { + for (auto& node_update : node_changes) { + node_update.state_change = NodeStateChange::None; + } + for (auto& node_state : node_last_states) { + node_state = 0; + } +} + +State LANDiscovery::GetState() const { + return state; +} + +void LANDiscovery::SetState(State new_state) { + state = new_state; +} + +Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network) const { + if (state == State::AccessPointCreated || state == State::StationConnected) { + std::memcpy(&out_network, &network_info, sizeof(network_info)); + return ResultSuccess; + } + + return ResultBadState; +} + +Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network, + std::vector<NodeLatestUpdate>& out_updates, + std::size_t buffer_count) { + if (buffer_count > NodeCountMax) { + return ResultInvalidBufferCount; + } + + if (state == State::AccessPointCreated || state == State::StationConnected) { + std::memcpy(&out_network, &network_info, sizeof(network_info)); + for (std::size_t i = 0; i < buffer_count; i++) { + out_updates[i].state_change = node_changes[i].state_change; + node_changes[i].state_change = NodeStateChange::None; + } + return ResultSuccess; + } + + return ResultBadState; +} + +DisconnectReason LANDiscovery::GetDisconnectReason() const { + return disconnect_reason; +} + +Result LANDiscovery::Scan(std::vector<NetworkInfo>& networks, u16& count, + const ScanFilter& filter) { + if (!IsFlagSet(filter.flag, ScanFilterFlag::NetworkType) || + filter.network_type <= NetworkType::All) { + if (!IsFlagSet(filter.flag, ScanFilterFlag::Ssid) && filter.ssid.length >= SsidLengthMax) { + return ResultBadInput; + } + } + + { + std::scoped_lock lock{packet_mutex}; + scan_results.clear(); + + SendBroadcast(Network::LDNPacketType::Scan); + } + + LOG_INFO(Service_LDN, "Waiting for scan replies"); + std::this_thread::sleep_for(std::chrono::seconds(1)); + + std::scoped_lock lock{packet_mutex}; + for (const auto& [key, info] : scan_results) { + if (count >= networks.size()) { + break; + } + + if (IsFlagSet(filter.flag, ScanFilterFlag::LocalCommunicationId)) { + if (filter.network_id.intent_id.local_communication_id != + info.network_id.intent_id.local_communication_id) { + continue; + } + } + if (IsFlagSet(filter.flag, ScanFilterFlag::SessionId)) { + if (filter.network_id.session_id != info.network_id.session_id) { + continue; + } + } + if (IsFlagSet(filter.flag, ScanFilterFlag::NetworkType)) { + if (filter.network_type != static_cast<NetworkType>(info.common.network_type)) { + continue; + } + } + if (IsFlagSet(filter.flag, ScanFilterFlag::Ssid)) { + if (filter.ssid != info.common.ssid) { + continue; + } + } + if (IsFlagSet(filter.flag, ScanFilterFlag::SceneId)) { + if (filter.network_id.intent_id.scene_id != info.network_id.intent_id.scene_id) { + continue; + } + } + + networks[count++] = info; + } + + return ResultSuccess; +} + +Result LANDiscovery::SetAdvertiseData(std::span<const u8> data) { + std::scoped_lock lock{packet_mutex}; + const std::size_t size = data.size(); + if (size > AdvertiseDataSizeMax) { + return ResultAdvertiseDataTooLarge; + } + + std::memcpy(network_info.ldn.advertise_data.data(), data.data(), size); + network_info.ldn.advertise_data_size = static_cast<u16>(size); + + UpdateNodes(); + + return ResultSuccess; +} + +Result LANDiscovery::OpenAccessPoint() { + std::scoped_lock lock{packet_mutex}; + disconnect_reason = DisconnectReason::None; + if (state == State::None) { + return ResultBadState; + } + + ResetStations(); + SetState(State::AccessPointOpened); + + return ResultSuccess; +} + +Result LANDiscovery::CloseAccessPoint() { + std::scoped_lock lock{packet_mutex}; + if (state == State::None) { + return ResultBadState; + } + + if (state == State::AccessPointCreated) { + DestroyNetwork(); + } + + ResetStations(); + SetState(State::Initialized); + + return ResultSuccess; +} + +Result LANDiscovery::OpenStation() { + std::scoped_lock lock{packet_mutex}; + disconnect_reason = DisconnectReason::None; + if (state == State::None) { + return ResultBadState; + } + + ResetStations(); + SetState(State::StationOpened); + + return ResultSuccess; +} + +Result LANDiscovery::CloseStation() { + std::scoped_lock lock{packet_mutex}; + if (state == State::None) { + return ResultBadState; + } + + if (state == State::StationConnected) { + Disconnect(); + } + + ResetStations(); + SetState(State::Initialized); + + return ResultSuccess; +} + +Result LANDiscovery::CreateNetwork(const SecurityConfig& security_config, + const UserConfig& user_config, + const NetworkConfig& network_config) { + std::scoped_lock lock{packet_mutex}; + + if (state != State::AccessPointOpened) { + return ResultBadState; + } + + InitNetworkInfo(); + network_info.ldn.node_count_max = network_config.node_count_max; + network_info.ldn.security_mode = security_config.security_mode; + + if (network_config.channel == WifiChannel::Default) { + network_info.common.channel = WifiChannel::Wifi24_6; + } else { + network_info.common.channel = network_config.channel; + } + + std::independent_bits_engine<std::mt19937, 64, u64> bits_engine; + network_info.network_id.session_id.high = bits_engine(); + network_info.network_id.session_id.low = bits_engine(); + network_info.network_id.intent_id = network_config.intent_id; + + NodeInfo& node0 = network_info.ldn.nodes[0]; + const Result rc2 = GetNodeInfo(node0, user_config, network_config.local_communication_version); + if (rc2.IsError()) { + return ResultAccessPointConnectionFailed; + } + + SetState(State::AccessPointCreated); + + InitNodeStateChange(); + node0.is_connected = 1; + UpdateNodes(); + + return rc2; +} + +Result LANDiscovery::DestroyNetwork() { + for (auto local_ip : connected_clients) { + SendPacket(Network::LDNPacketType::DestroyNetwork, local_ip); + } + + ResetStations(); + + SetState(State::AccessPointOpened); + lan_event(); + + return ResultSuccess; +} + +Result LANDiscovery::Connect(const NetworkInfo& network_info_, const UserConfig& user_config, + u16 local_communication_version) { + std::scoped_lock lock{packet_mutex}; + if (network_info_.ldn.node_count == 0) { + return ResultInvalidNodeCount; + } + + Result rc = GetNodeInfo(node_info, user_config, local_communication_version); + if (rc.IsError()) { + return ResultConnectionFailed; + } + + Ipv4Address node_host = network_info_.ldn.nodes[0].ipv4_address; + std::reverse(std::begin(node_host), std::end(node_host)); // htonl + host_ip = node_host; + SendPacket(Network::LDNPacketType::Connect, node_info, *host_ip); + + InitNodeStateChange(); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + return ResultSuccess; +} + +Result LANDiscovery::Disconnect() { + if (host_ip) { + SendPacket(Network::LDNPacketType::Disconnect, node_info, *host_ip); + } + + SetState(State::StationOpened); + lan_event(); + + return ResultSuccess; +} + +Result LANDiscovery::Initialize(LanEventFunc lan_event_, bool listening) { + std::scoped_lock lock{packet_mutex}; + if (inited) { + return ResultSuccess; + } + + for (auto& station : stations) { + station.discovery = this; + station.node_info = &network_info.ldn.nodes[station.node_id]; + station.Reset(); + } + + connected_clients.clear(); + lan_event = lan_event_; + + SetState(State::Initialized); + + inited = true; + return ResultSuccess; +} + +Result LANDiscovery::Finalize() { + std::scoped_lock lock{packet_mutex}; + Result rc = ResultSuccess; + + if (inited) { + if (state == State::AccessPointCreated) { + DestroyNetwork(); + } + if (state == State::StationConnected) { + Disconnect(); + } + + ResetStations(); + inited = false; + } + + SetState(State::None); + + return rc; +} + +void LANDiscovery::ResetStations() { + for (auto& station : stations) { + station.Reset(); + } + connected_clients.clear(); +} + +void LANDiscovery::UpdateNodes() { + u8 count = 0; + for (auto& station : stations) { + bool connected = station.GetStatus() == NodeStatus::Connected; + if (connected) { + count++; + } + station.OverrideInfo(); + } + network_info.ldn.node_count = count + 1; + + for (auto local_ip : connected_clients) { + SendPacket(Network::LDNPacketType::SyncNetwork, network_info, local_ip); + } + + OnNetworkInfoChanged(); +} + +void LANDiscovery::OnSyncNetwork(const NetworkInfo& info) { + network_info = info; + if (state == State::StationOpened) { + SetState(State::StationConnected); + } + OnNetworkInfoChanged(); +} + +void LANDiscovery::OnDisconnectFromHost() { + LOG_INFO(Service_LDN, "OnDisconnectFromHost state: {}", static_cast<int>(state)); + host_ip = std::nullopt; + if (state == State::StationConnected) { + SetState(State::StationOpened); + lan_event(); + } +} + +void LANDiscovery::OnNetworkInfoChanged() { + if (IsNodeStateChanged()) { + lan_event(); + } + return; +} + +Network::IPv4Address LANDiscovery::GetLocalIp() const { + Network::IPv4Address local_ip{0xFF, 0xFF, 0xFF, 0xFF}; + if (auto room_member = room_network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + local_ip = room_member->GetFakeIpAddress(); + } + } + return local_ip; +} + +template <typename Data> +void LANDiscovery::SendPacket(Network::LDNPacketType type, const Data& data, + Ipv4Address remote_ip) { + Network::LDNPacket packet; + packet.type = type; + + packet.broadcast = false; + packet.local_ip = GetLocalIp(); + packet.remote_ip = remote_ip; + + packet.data.resize(sizeof(data)); + std::memcpy(packet.data.data(), &data, sizeof(data)); + SendPacket(packet); +} + +void LANDiscovery::SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip) { + Network::LDNPacket packet; + packet.type = type; + + packet.broadcast = false; + packet.local_ip = GetLocalIp(); + packet.remote_ip = remote_ip; + + SendPacket(packet); +} + +template <typename Data> +void LANDiscovery::SendBroadcast(Network::LDNPacketType type, const Data& data) { + Network::LDNPacket packet; + packet.type = type; + + packet.broadcast = true; + packet.local_ip = GetLocalIp(); + + packet.data.resize(sizeof(data)); + std::memcpy(packet.data.data(), &data, sizeof(data)); + SendPacket(packet); +} + +void LANDiscovery::SendBroadcast(Network::LDNPacketType type) { + Network::LDNPacket packet; + packet.type = type; + + packet.broadcast = true; + packet.local_ip = GetLocalIp(); + + SendPacket(packet); +} + +void LANDiscovery::SendPacket(const Network::LDNPacket& packet) { + if (auto room_member = room_network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + room_member->SendLdnPacket(packet); + } + } +} + +void LANDiscovery::ReceivePacket(const Network::LDNPacket& packet) { + std::scoped_lock lock{packet_mutex}; + switch (packet.type) { + case Network::LDNPacketType::Scan: { + LOG_INFO(Frontend, "Scan packet received!"); + if (state == State::AccessPointCreated) { + // Reply to the sender + SendPacket(Network::LDNPacketType::ScanResp, network_info, packet.local_ip); + } + break; + } + case Network::LDNPacketType::ScanResp: { + LOG_INFO(Frontend, "ScanResp packet received!"); + + NetworkInfo info{}; + std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo)); + scan_results.insert({info.common.bssid, info}); + + break; + } + case Network::LDNPacketType::Connect: { + LOG_INFO(Frontend, "Connect packet received!"); + + NodeInfo info{}; + std::memcpy(&info, packet.data.data(), sizeof(NodeInfo)); + + connected_clients.push_back(packet.local_ip); + + for (LanStation& station : stations) { + if (station.status != NodeStatus::Connected) { + *station.node_info = info; + station.status = NodeStatus::Connected; + break; + } + } + + UpdateNodes(); + + break; + } + case Network::LDNPacketType::Disconnect: { + LOG_INFO(Frontend, "Disconnect packet received!"); + + connected_clients.erase( + std::remove(connected_clients.begin(), connected_clients.end(), packet.local_ip), + connected_clients.end()); + + NodeInfo info{}; + std::memcpy(&info, packet.data.data(), sizeof(NodeInfo)); + + for (LanStation& station : stations) { + if (station.status == NodeStatus::Connected && + station.node_info->mac_address == info.mac_address) { + station.OnClose(); + break; + } + } + + break; + } + case Network::LDNPacketType::DestroyNetwork: { + ResetStations(); + OnDisconnectFromHost(); + break; + } + case Network::LDNPacketType::SyncNetwork: { + if (state == State::StationOpened || state == State::StationConnected) { + LOG_INFO(Frontend, "SyncNetwork packet received!"); + NetworkInfo info{}; + std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo)); + + OnSyncNetwork(info); + } else { + LOG_INFO(Frontend, "SyncNetwork packet received but in wrong State!"); + } + + break; + } + default: { + LOG_INFO(Frontend, "ReceivePacket unhandled type {}", static_cast<int>(packet.type)); + break; + } + } +} + +bool LANDiscovery::IsNodeStateChanged() { + bool changed = false; + const auto& nodes = network_info.ldn.nodes; + for (int i = 0; i < NodeCountMax; i++) { + if (nodes[i].is_connected != node_last_states[i]) { + if (nodes[i].is_connected) { + node_changes[i].state_change |= NodeStateChange::Connect; + } else { + node_changes[i].state_change |= NodeStateChange::Disconnect; + } + node_last_states[i] = nodes[i].is_connected; + changed = true; + } + } + return changed; +} + +bool LANDiscovery::IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const { + const auto flag_value = static_cast<u32>(flag); + const auto search_flag_value = static_cast<u32>(search_flag); + return (flag_value & search_flag_value) == search_flag_value; +} + +int LANDiscovery::GetStationCount() const { + return static_cast<int>( + std::count_if(stations.begin(), stations.end(), [](const auto& station) { + return station.GetStatus() != NodeStatus::Disconnected; + })); +} + +MacAddress LANDiscovery::GetFakeMac() const { + MacAddress mac{}; + mac.raw[0] = 0x02; + mac.raw[1] = 0x00; + + const auto ip = GetLocalIp(); + memcpy(mac.raw.data() + 2, &ip, sizeof(ip)); + + return mac; +} + +Result LANDiscovery::GetNodeInfo(NodeInfo& node, const UserConfig& userConfig, + u16 localCommunicationVersion) { + const auto network_interface = Network::GetSelectedNetworkInterface(); + + if (!network_interface) { + LOG_ERROR(Service_LDN, "No network interface available"); + return ResultNoIpAddress; + } + + node.mac_address = GetFakeMac(); + node.is_connected = 1; + std::memcpy(node.user_name.data(), userConfig.user_name.data(), UserNameBytesMax + 1); + node.local_communication_version = localCommunicationVersion; + + Ipv4Address current_address = GetLocalIp(); + std::reverse(std::begin(current_address), std::end(current_address)); // ntohl + node.ipv4_address = current_address; + + return ResultSuccess; +} + +} // namespace Service::LDN diff --git a/src/core/hle/service/ldn/lan_discovery.h b/src/core/hle/service/ldn/lan_discovery.h new file mode 100644 index 000000000..3833cd764 --- /dev/null +++ b/src/core/hle/service/ldn/lan_discovery.h @@ -0,0 +1,134 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <cstring> +#include <functional> +#include <memory> +#include <mutex> +#include <optional> +#include <random> +#include <span> +#include <thread> +#include <unordered_map> + +#include "common/logging/log.h" +#include "common/socket_types.h" +#include "core/hle/result.h" +#include "core/hle/service/ldn/ldn_results.h" +#include "core/hle/service/ldn/ldn_types.h" +#include "network/network.h" + +namespace Service::LDN { + +class LANDiscovery; + +class LanStation { +public: + LanStation(s8 node_id_, LANDiscovery* discovery_); + ~LanStation(); + + void OnClose(); + NodeStatus GetStatus() const; + void Reset(); + void OverrideInfo(); + +protected: + friend class LANDiscovery; + NodeInfo* node_info; + NodeStatus status; + s8 node_id; + LANDiscovery* discovery; +}; + +class LANDiscovery { +public: + using LanEventFunc = std::function<void()>; + + LANDiscovery(Network::RoomNetwork& room_network_); + ~LANDiscovery(); + + State GetState() const; + void SetState(State new_state); + + Result GetNetworkInfo(NetworkInfo& out_network) const; + Result GetNetworkInfo(NetworkInfo& out_network, std::vector<NodeLatestUpdate>& out_updates, + std::size_t buffer_count); + + DisconnectReason GetDisconnectReason() const; + Result Scan(std::vector<NetworkInfo>& networks, u16& count, const ScanFilter& filter); + Result SetAdvertiseData(std::span<const u8> data); + + Result OpenAccessPoint(); + Result CloseAccessPoint(); + + Result OpenStation(); + Result CloseStation(); + + Result CreateNetwork(const SecurityConfig& security_config, const UserConfig& user_config, + const NetworkConfig& network_config); + Result DestroyNetwork(); + + Result Connect(const NetworkInfo& network_info_, const UserConfig& user_config, + u16 local_communication_version); + Result Disconnect(); + + Result Initialize(LanEventFunc lan_event_ = empty_func, bool listening = true); + Result Finalize(); + + void ReceivePacket(const Network::LDNPacket& packet); + +protected: + friend class LanStation; + + void InitNetworkInfo(); + void InitNodeStateChange(); + + void ResetStations(); + void UpdateNodes(); + + void OnSyncNetwork(const NetworkInfo& info); + void OnDisconnectFromHost(); + void OnNetworkInfoChanged(); + + bool IsNodeStateChanged(); + bool IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const; + int GetStationCount() const; + MacAddress GetFakeMac() const; + Result GetNodeInfo(NodeInfo& node, const UserConfig& user_config, + u16 local_communication_version); + + Network::IPv4Address GetLocalIp() const; + template <typename Data> + void SendPacket(Network::LDNPacketType type, const Data& data, Ipv4Address remote_ip); + void SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip); + template <typename Data> + void SendBroadcast(Network::LDNPacketType type, const Data& data); + void SendBroadcast(Network::LDNPacketType type); + void SendPacket(const Network::LDNPacket& packet); + + static const LanEventFunc empty_func; + static constexpr Ssid fake_ssid{"YuzuFakeSsidForLdn"}; + + bool inited{}; + std::mutex packet_mutex; + std::array<LanStation, StationCountMax> stations; + std::array<NodeLatestUpdate, NodeCountMax> node_changes{}; + std::array<u8, NodeCountMax> node_last_states{}; + std::unordered_map<MacAddress, NetworkInfo, MACAddressHash> scan_results{}; + NodeInfo node_info{}; + NetworkInfo network_info{}; + State state{State::None}; + DisconnectReason disconnect_reason{DisconnectReason::None}; + + // TODO (flTobi): Should this be an std::set? + std::vector<Ipv4Address> connected_clients; + std::optional<Ipv4Address> host_ip; + + LanEventFunc lan_event; + + Network::RoomNetwork& room_network; +}; +} // namespace Service::LDN diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index 6ccbe9623..ea3e7e55a 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp @@ -1,14 +1,19 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> -#include "core/hle/ipc_helpers.h" -#include "core/hle/result.h" -#include "core/hle/service/ldn/errors.h" +#include "core/core.h" +#include "core/hle/service/ldn/lan_discovery.h" #include "core/hle/service/ldn/ldn.h" -#include "core/hle/service/sm/sm.h" +#include "core/hle/service/ldn/ldn_results.h" +#include "core/hle/service/ldn/ldn_types.h" +#include "core/internal_network/network.h" +#include "core/internal_network/network_interface.h" +#include "network/network.h" + +// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent +#undef CreateEvent namespace Service::LDN { @@ -101,74 +106,445 @@ class IUserLocalCommunicationService final : public ServiceFramework<IUserLocalCommunicationService> { public: explicit IUserLocalCommunicationService(Core::System& system_) - : ServiceFramework{system_, "IUserLocalCommunicationService"} { + : ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew}, + service_context{system, "IUserLocalCommunicationService"}, + room_network{system_.GetRoomNetwork()}, lan_discovery{room_network} { // clang-format off static const FunctionInfo functions[] = { {0, &IUserLocalCommunicationService::GetState, "GetState"}, - {1, nullptr, "GetNetworkInfo"}, - {2, nullptr, "GetIpv4Address"}, - {3, nullptr, "GetDisconnectReason"}, - {4, nullptr, "GetSecurityParameter"}, - {5, nullptr, "GetNetworkConfig"}, - {100, nullptr, "AttachStateChangeEvent"}, - {101, nullptr, "GetNetworkInfoLatestUpdate"}, - {102, nullptr, "Scan"}, - {103, nullptr, "ScanPrivate"}, - {104, nullptr, "SetWirelessControllerRestriction"}, - {200, nullptr, "OpenAccessPoint"}, - {201, nullptr, "CloseAccessPoint"}, - {202, nullptr, "CreateNetwork"}, - {203, nullptr, "CreateNetworkPrivate"}, - {204, nullptr, "DestroyNetwork"}, + {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"}, + {2, &IUserLocalCommunicationService::GetIpv4Address, "GetIpv4Address"}, + {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"}, + {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"}, + {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"}, + {100, &IUserLocalCommunicationService::AttachStateChangeEvent, "AttachStateChangeEvent"}, + {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"}, + {102, &IUserLocalCommunicationService::Scan, "Scan"}, + {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"}, + {104, &IUserLocalCommunicationService::SetWirelessControllerRestriction, "SetWirelessControllerRestriction"}, + {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"}, + {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"}, + {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"}, + {203, &IUserLocalCommunicationService::CreateNetworkPrivate, "CreateNetworkPrivate"}, + {204, &IUserLocalCommunicationService::DestroyNetwork, "DestroyNetwork"}, {205, nullptr, "Reject"}, - {206, nullptr, "SetAdvertiseData"}, - {207, nullptr, "SetStationAcceptPolicy"}, - {208, nullptr, "AddAcceptFilterEntry"}, + {206, &IUserLocalCommunicationService::SetAdvertiseData, "SetAdvertiseData"}, + {207, &IUserLocalCommunicationService::SetStationAcceptPolicy, "SetStationAcceptPolicy"}, + {208, &IUserLocalCommunicationService::AddAcceptFilterEntry, "AddAcceptFilterEntry"}, {209, nullptr, "ClearAcceptFilter"}, - {300, nullptr, "OpenStation"}, - {301, nullptr, "CloseStation"}, - {302, nullptr, "Connect"}, + {300, &IUserLocalCommunicationService::OpenStation, "OpenStation"}, + {301, &IUserLocalCommunicationService::CloseStation, "CloseStation"}, + {302, &IUserLocalCommunicationService::Connect, "Connect"}, {303, nullptr, "ConnectPrivate"}, - {304, nullptr, "Disconnect"}, - {400, nullptr, "Initialize"}, - {401, nullptr, "Finalize"}, - {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, // 7.0.0+ + {304, &IUserLocalCommunicationService::Disconnect, "Disconnect"}, + {400, &IUserLocalCommunicationService::Initialize, "Initialize"}, + {401, &IUserLocalCommunicationService::Finalize, "Finalize"}, + {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, }; // clang-format on RegisterHandlers(functions); + + state_change_event = + service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent"); + } + + ~IUserLocalCommunicationService() { + if (is_initialized) { + if (auto room_member = room_network.GetRoomMember().lock()) { + room_member->Unbind(ldn_packet_received); + } + } + + service_context.CloseEvent(state_change_event); + } + + /// Callback to parse and handle a received LDN packet. + void OnLDNPacketReceived(const Network::LDNPacket& packet) { + lan_discovery.ReceivePacket(packet); + } + + void OnEventFired() { + state_change_event->GetWritableEvent().Signal(); } void GetState(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_LDN, "(STUBBED) called"); + State state = State::Error; + + if (is_initialized) { + state = lan_discovery.GetState(); + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(state); + } + + void GetNetworkInfo(Kernel::HLERequestContext& ctx) { + const auto write_buffer_size = ctx.GetWriteBufferSize(); + + if (write_buffer_size != sizeof(NetworkInfo)) { + LOG_ERROR(Service_LDN, "Invalid buffer size {}", write_buffer_size); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultBadInput); + return; + } + + NetworkInfo network_info{}; + const auto rc = lan_discovery.GetNetworkInfo(network_info); + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + return; + } + + ctx.WriteBuffer<NetworkInfo>(network_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + void GetIpv4Address(Kernel::HLERequestContext& ctx) { + const auto network_interface = Network::GetSelectedNetworkInterface(); + + if (!network_interface) { + LOG_ERROR(Service_LDN, "No network interface available"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNoIpAddress); + return; + } + + Ipv4Address current_address{Network::TranslateIPv4(network_interface->ip_address)}; + Ipv4Address subnet_mask{Network::TranslateIPv4(network_interface->subnet_mask)}; + + // When we're connected to a room, spoof the hosts IP address + if (auto room_member = room_network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + current_address = room_member->GetFakeIpAddress(); + } + } + + std::reverse(std::begin(current_address), std::end(current_address)); // ntohl + std::reverse(std::begin(subnet_mask), std::end(subnet_mask)); // ntohl + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushRaw(current_address); + rb.PushRaw(subnet_mask); + } + + void GetDisconnectReason(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(lan_discovery.GetDisconnectReason()); + } - // Indicate a network error, as we do not actually emulate LDN - rb.Push(static_cast<u32>(State::Error)); + void GetSecurityParameter(Kernel::HLERequestContext& ctx) { + SecurityParameter security_parameter{}; + NetworkInfo info{}; + const Result rc = lan_discovery.GetNetworkInfo(info); + + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + return; + } + + security_parameter.session_id = info.network_id.session_id; + std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(), + sizeof(SecurityParameter::data)); + + IPC::ResponseBuilder rb{ctx, 10}; + rb.Push(rc); + rb.PushRaw<SecurityParameter>(security_parameter); + } + void GetNetworkConfig(Kernel::HLERequestContext& ctx) { + NetworkConfig config{}; + NetworkInfo info{}; + const Result rc = lan_discovery.GetNetworkInfo(info); + + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + return; + } + + config.intent_id = info.network_id.intent_id; + config.channel = info.common.channel; + config.node_count_max = info.ldn.node_count_max; + config.local_communication_version = info.ldn.nodes[0].local_communication_version; + + IPC::ResponseBuilder rb{ctx, 10}; + rb.Push(rc); + rb.PushRaw<NetworkConfig>(config); + } + + void AttachStateChangeEvent(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); + rb.PushCopyObjects(state_change_event->GetReadableEvent()); } - void Initialize2(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_LDN, "called"); + void GetNetworkInfoLatestUpdate(Kernel::HLERequestContext& ctx) { + const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0); + const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate); - is_initialized = true; + if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) { + LOG_ERROR(Service_LDN, "Invalid buffer, size = {}, count = {}", network_buffer_size, + node_buffer_count); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultBadInput); + return; + } + + NetworkInfo info{}; + std::vector<NodeLatestUpdate> latest_update(node_buffer_count); + + const auto rc = lan_discovery.GetNetworkInfo(info, latest_update, latest_update.size()); + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + return; + } + + ctx.WriteBuffer(info, 0); + ctx.WriteBuffer(latest_update, 1); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_DISABLED); + rb.Push(ResultSuccess); + } + + void Scan(Kernel::HLERequestContext& ctx) { + ScanImpl(ctx); + } + + void ScanPrivate(Kernel::HLERequestContext& ctx) { + ScanImpl(ctx, true); + } + + void ScanImpl(Kernel::HLERequestContext& ctx, bool is_private = false) { + IPC::RequestParser rp{ctx}; + const auto channel{rp.PopEnum<WifiChannel>()}; + const auto scan_filter{rp.PopRaw<ScanFilter>()}; + + const std::size_t network_info_size = ctx.GetWriteBufferSize() / sizeof(NetworkInfo); + + if (network_info_size == 0) { + LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultBadInput); + return; + } + + u16 count = 0; + std::vector<NetworkInfo> network_infos(network_info_size); + Result rc = lan_discovery.Scan(network_infos, count, scan_filter); + + LOG_INFO(Service_LDN, + "called, channel={}, filter_scan_flag={}, filter_network_type={}, is_private={}", + channel, scan_filter.flag, scan_filter.network_type, is_private); + + ctx.WriteBuffer(network_infos); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(rc); + rb.Push<u32>(count); + } + + void SetWirelessControllerRestriction(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); } -private: - enum class State { - None, - Initialized, - AccessPointOpened, - AccessPointCreated, - StationOpened, - StationConnected, - Error, - }; + void OpenAccessPoint(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.OpenAccessPoint()); + } + + void CloseAccessPoint(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.CloseAccessPoint()); + } + + void CreateNetwork(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + CreateNetworkImpl(ctx); + } + + void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + CreateNetworkImpl(ctx, true); + } + + void CreateNetworkImpl(Kernel::HLERequestContext& ctx, bool is_private = false) { + IPC::RequestParser rp{ctx}; + + const auto security_config{rp.PopRaw<SecurityConfig>()}; + [[maybe_unused]] const auto security_parameter{is_private ? rp.PopRaw<SecurityParameter>() + : SecurityParameter{}}; + const auto user_config{rp.PopRaw<UserConfig>()}; + rp.Pop<u32>(); // Padding + const auto network_Config{rp.PopRaw<NetworkConfig>()}; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.CreateNetwork(security_config, user_config, network_Config)); + } + + void DestroyNetwork(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.DestroyNetwork()); + } + + void SetAdvertiseData(Kernel::HLERequestContext& ctx) { + std::vector<u8> read_buffer = ctx.ReadBuffer(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.SetAdvertiseData(read_buffer)); + } + + void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void AddAcceptFilterEntry(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void OpenStation(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.OpenStation()); + } + + void CloseStation(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.CloseStation()); + } + + void Connect(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + SecurityConfig security_config; + UserConfig user_config; + u32 local_communication_version; + u32 option; + }; + static_assert(sizeof(Parameters) == 0x7C, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_INFO(Service_LDN, + "called, passphrase_size={}, security_mode={}, " + "local_communication_version={}", + parameters.security_config.passphrase_size, + parameters.security_config.security_mode, parameters.local_communication_version); + + const std::vector<u8> read_buffer = ctx.ReadBuffer(); + if (read_buffer.size() != sizeof(NetworkInfo)) { + LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultBadInput); + return; + } + + NetworkInfo network_info{}; + std::memcpy(&network_info, read_buffer.data(), read_buffer.size()); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.Connect(network_info, parameters.user_config, + static_cast<u16>(parameters.local_communication_version))); + } + + void Disconnect(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.Disconnect()); + } + + void Initialize(Kernel::HLERequestContext& ctx) { + const auto rc = InitializeImpl(ctx); + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + } + + void Finalize(Kernel::HLERequestContext& ctx) { + if (auto room_member = room_network.GetRoomMember().lock()) { + room_member->Unbind(ldn_packet_received); + } + + is_initialized = false; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.Finalize()); + } + + void Initialize2(Kernel::HLERequestContext& ctx) { + const auto rc = InitializeImpl(ctx); + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + } + + Result InitializeImpl(Kernel::HLERequestContext& ctx) { + const auto network_interface = Network::GetSelectedNetworkInterface(); + if (!network_interface) { + LOG_ERROR(Service_LDN, "No network interface is set"); + return ResultAirplaneModeEnabled; + } + + if (auto room_member = room_network.GetRoomMember().lock()) { + ldn_packet_received = room_member->BindOnLdnPacketReceived( + [this](const Network::LDNPacket& packet) { OnLDNPacketReceived(packet); }); + } else { + LOG_ERROR(Service_LDN, "Couldn't bind callback!"); + return ResultAirplaneModeEnabled; + } + + lan_discovery.Initialize([&]() { OnEventFired(); }); + is_initialized = true; + return ResultSuccess; + } + + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* state_change_event; + Network::RoomNetwork& room_network; + LANDiscovery lan_discovery; + + // Callback identifier for the OnLDNPacketReceived event. + Network::RoomMember::CallbackHandle<Network::LDNPacket> ldn_packet_received; bool is_initialized{}; }; @@ -274,7 +650,7 @@ public: LOG_WARNING(Service_LDN, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_DISABLED); + rb.Push(ResultDisabled); } }; diff --git a/src/core/hle/service/ldn/ldn.h b/src/core/hle/service/ldn/ldn.h index 3ccd9738b..6afe2ea6f 100644 --- a/src/core/hle/service/ldn/ldn.h +++ b/src/core/hle/service/ldn/ldn.h @@ -1,9 +1,14 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/result.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/sm/sm.h" + namespace Core { class System; } diff --git a/src/core/hle/service/ldn/ldn_results.h b/src/core/hle/service/ldn/ldn_results.h new file mode 100644 index 000000000..f340bda42 --- /dev/null +++ b/src/core/hle/service/ldn/ldn_results.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::LDN { + +constexpr Result ResultAdvertiseDataTooLarge{ErrorModule::LDN, 10}; +constexpr Result ResultAuthenticationFailed{ErrorModule::LDN, 20}; +constexpr Result ResultDisabled{ErrorModule::LDN, 22}; +constexpr Result ResultAirplaneModeEnabled{ErrorModule::LDN, 23}; +constexpr Result ResultInvalidNodeCount{ErrorModule::LDN, 30}; +constexpr Result ResultConnectionFailed{ErrorModule::LDN, 31}; +constexpr Result ResultBadState{ErrorModule::LDN, 32}; +constexpr Result ResultNoIpAddress{ErrorModule::LDN, 33}; +constexpr Result ResultInvalidBufferCount{ErrorModule::LDN, 50}; +constexpr Result ResultAccessPointConnectionFailed{ErrorModule::LDN, 65}; +constexpr Result ResultAuthenticationTimeout{ErrorModule::LDN, 66}; +constexpr Result ResultMaximumNodeCount{ErrorModule::LDN, 67}; +constexpr Result ResultBadInput{ErrorModule::LDN, 96}; +constexpr Result ResultLocalCommunicationIdNotFound{ErrorModule::LDN, 97}; +constexpr Result ResultLocalCommunicationVersionTooLow{ErrorModule::LDN, 113}; +constexpr Result ResultLocalCommunicationVersionTooHigh{ErrorModule::LDN, 114}; + +} // namespace Service::LDN diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h new file mode 100644 index 000000000..44c2c773b --- /dev/null +++ b/src/core/hle/service/ldn/ldn_types.h @@ -0,0 +1,306 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <fmt/format.h> + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "network/network.h" + +namespace Service::LDN { + +constexpr size_t SsidLengthMax = 32; +constexpr size_t AdvertiseDataSizeMax = 384; +constexpr size_t UserNameBytesMax = 32; +constexpr int NodeCountMax = 8; +constexpr int StationCountMax = NodeCountMax - 1; +constexpr size_t PassphraseLengthMax = 64; + +enum class SecurityMode : u16 { + All, + Retail, + Debug, +}; + +enum class NodeStateChange : u8 { + None, + Connect, + Disconnect, + DisconnectAndConnect, +}; + +DECLARE_ENUM_FLAG_OPERATORS(NodeStateChange) + +enum class ScanFilterFlag : u32 { + None = 0, + LocalCommunicationId = 1 << 0, + SessionId = 1 << 1, + NetworkType = 1 << 2, + Ssid = 1 << 4, + SceneId = 1 << 5, + IntentId = LocalCommunicationId | SceneId, + NetworkId = IntentId | SessionId, +}; + +enum class NetworkType : u32 { + None, + General, + Ldn, + All, +}; + +enum class PackedNetworkType : u8 { + None, + General, + Ldn, + All, +}; + +enum class State : u32 { + None, + Initialized, + AccessPointOpened, + AccessPointCreated, + StationOpened, + StationConnected, + Error, +}; + +enum class DisconnectReason : s16 { + Unknown = -1, + None, + DisconnectedByUser, + DisconnectedBySystem, + DestroyedByUser, + DestroyedBySystem, + Rejected, + SignalLost, +}; + +enum class NetworkError { + Unknown = -1, + None = 0, + PortUnreachable, + TooManyPlayers, + VersionTooLow, + VersionTooHigh, + ConnectFailure, + ConnectNotFound, + ConnectTimeout, + ConnectRejected, + RejectFailed, +}; + +enum class AcceptPolicy : u8 { + AcceptAll, + RejectAll, + BlackList, + WhiteList, +}; + +enum class WifiChannel : s16 { + Default = 0, + Wifi24_1 = 1, + Wifi24_6 = 6, + Wifi24_11 = 11, + Wifi50_36 = 36, + Wifi50_40 = 40, + Wifi50_44 = 44, + Wifi50_48 = 48, +}; + +enum class LinkLevel : s8 { + Bad, + Low, + Good, + Excellent, +}; + +enum class NodeStatus : u8 { + Disconnected, + Connected, +}; + +struct NodeLatestUpdate { + NodeStateChange state_change; + INSERT_PADDING_BYTES(0x7); // Unknown +}; +static_assert(sizeof(NodeLatestUpdate) == 0x8, "NodeLatestUpdate is an invalid size"); + +struct SessionId { + u64 high; + u64 low; + + bool operator==(const SessionId&) const = default; +}; +static_assert(sizeof(SessionId) == 0x10, "SessionId is an invalid size"); + +struct IntentId { + u64 local_communication_id; + INSERT_PADDING_BYTES(0x2); // Reserved + u16 scene_id; + INSERT_PADDING_BYTES(0x4); // Reserved +}; +static_assert(sizeof(IntentId) == 0x10, "IntentId is an invalid size"); + +struct NetworkId { + IntentId intent_id; + SessionId session_id; +}; +static_assert(sizeof(NetworkId) == 0x20, "NetworkId is an invalid size"); + +struct Ssid { + u8 length{}; + std::array<char, SsidLengthMax + 1> raw{}; + + Ssid() = default; + + constexpr explicit Ssid(std::string_view data) { + length = static_cast<u8>(std::min(data.size(), SsidLengthMax)); + data.copy(raw.data(), length); + raw[length] = 0; + } + + std::string GetStringValue() const { + return std::string(raw.data()); + } + + bool operator==(const Ssid& b) const { + return (length == b.length) && (std::memcmp(raw.data(), b.raw.data(), length) == 0); + } + + bool operator!=(const Ssid& b) const { + return !operator==(b); + } +}; +static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size"); + +using Ipv4Address = std::array<u8, 4>; +static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size"); + +struct MacAddress { + std::array<u8, 6> raw{}; + + friend bool operator==(const MacAddress& lhs, const MacAddress& rhs) = default; +}; +static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size"); + +struct MACAddressHash { + size_t operator()(const MacAddress& address) const { + u64 value{}; + std::memcpy(&value, address.raw.data(), sizeof(address.raw)); + return value; + } +}; + +struct ScanFilter { + NetworkId network_id; + NetworkType network_type; + MacAddress mac_address; + Ssid ssid; + INSERT_PADDING_BYTES(0x10); + ScanFilterFlag flag; +}; +static_assert(sizeof(ScanFilter) == 0x60, "ScanFilter is an invalid size"); + +struct CommonNetworkInfo { + MacAddress bssid; + Ssid ssid; + WifiChannel channel; + LinkLevel link_level; + PackedNetworkType network_type; + INSERT_PADDING_BYTES(0x4); +}; +static_assert(sizeof(CommonNetworkInfo) == 0x30, "CommonNetworkInfo is an invalid size"); + +struct NodeInfo { + Ipv4Address ipv4_address; + MacAddress mac_address; + s8 node_id; + u8 is_connected; + std::array<u8, UserNameBytesMax + 1> user_name; + INSERT_PADDING_BYTES(0x1); // Reserved + s16 local_communication_version; + INSERT_PADDING_BYTES(0x10); // Reserved +}; +static_assert(sizeof(NodeInfo) == 0x40, "NodeInfo is an invalid size"); + +struct LdnNetworkInfo { + std::array<u8, 0x10> security_parameter; + SecurityMode security_mode; + AcceptPolicy station_accept_policy; + u8 has_action_frame; + INSERT_PADDING_BYTES(0x2); // Padding + u8 node_count_max; + u8 node_count; + std::array<NodeInfo, NodeCountMax> nodes; + INSERT_PADDING_BYTES(0x2); // Reserved + u16 advertise_data_size; + std::array<u8, AdvertiseDataSizeMax> advertise_data; + INSERT_PADDING_BYTES(0x8C); // Reserved + u64 random_authentication_id; +}; +static_assert(sizeof(LdnNetworkInfo) == 0x430, "LdnNetworkInfo is an invalid size"); + +struct NetworkInfo { + NetworkId network_id; + CommonNetworkInfo common; + LdnNetworkInfo ldn; +}; +static_assert(sizeof(NetworkInfo) == 0x480, "NetworkInfo is an invalid size"); + +struct SecurityConfig { + SecurityMode security_mode; + u16 passphrase_size; + std::array<u8, PassphraseLengthMax> passphrase; +}; +static_assert(sizeof(SecurityConfig) == 0x44, "SecurityConfig is an invalid size"); + +struct UserConfig { + std::array<u8, UserNameBytesMax + 1> user_name; + INSERT_PADDING_BYTES(0xF); // Reserved +}; +static_assert(sizeof(UserConfig) == 0x30, "UserConfig is an invalid size"); + +#pragma pack(push, 4) +struct ConnectRequest { + SecurityConfig security_config; + UserConfig user_config; + u32 local_communication_version; + u32 option_unknown; + NetworkInfo network_info; +}; +static_assert(sizeof(ConnectRequest) == 0x4FC, "ConnectRequest is an invalid size"); +#pragma pack(pop) + +struct SecurityParameter { + std::array<u8, 0x10> data; // Data, used with the same key derivation as SecurityConfig + SessionId session_id; +}; +static_assert(sizeof(SecurityParameter) == 0x20, "SecurityParameter is an invalid size"); + +struct NetworkConfig { + IntentId intent_id; + WifiChannel channel; + u8 node_count_max; + INSERT_PADDING_BYTES(0x1); // Reserved + u16 local_communication_version; + INSERT_PADDING_BYTES(0xA); // Reserved +}; +static_assert(sizeof(NetworkConfig) == 0x20, "NetworkConfig is an invalid size"); + +struct AddressEntry { + Ipv4Address ipv4_address; + MacAddress mac_address; + INSERT_PADDING_BYTES(0x2); // Reserved +}; +static_assert(sizeof(AddressEntry) == 0xC, "AddressEntry is an invalid size"); + +struct AddressList { + std::array<AddressEntry, 0x8> addresses; +}; +static_assert(sizeof(AddressList) == 0x60, "AddressList is an invalid size"); + +} // namespace Service::LDN diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 9fc7bb1b1..becd6d1b9 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> #include <fmt/format.h> @@ -12,7 +11,6 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_page_table.h" -#include "core/hle/kernel/k_system_control.h" #include "core/hle/kernel/svc_results.h" #include "core/hle/kernel/svc_types.h" #include "core/hle/service/ldr/ldr.h" @@ -22,20 +20,20 @@ namespace Service::LDR { -constexpr ResultCode ERROR_INSUFFICIENT_ADDRESS_SPACE{ErrorModule::RO, 2}; - -[[maybe_unused]] constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51}; -constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52}; -constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53}; -constexpr ResultCode ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54}; -constexpr ResultCode ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55}; -constexpr ResultCode ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56}; -constexpr ResultCode ERROR_ALREADY_LOADED{ErrorModule::Loader, 57}; -constexpr ResultCode ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81}; -constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::Loader, 82}; -constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84}; -[[maybe_unused]] constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85}; -constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87}; +constexpr Result ERROR_INSUFFICIENT_ADDRESS_SPACE{ErrorModule::RO, 2}; + +[[maybe_unused]] constexpr Result ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51}; +constexpr Result ERROR_INVALID_NRO{ErrorModule::Loader, 52}; +constexpr Result ERROR_INVALID_NRR{ErrorModule::Loader, 53}; +constexpr Result ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54}; +constexpr Result ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55}; +constexpr Result ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56}; +constexpr Result ERROR_ALREADY_LOADED{ErrorModule::Loader, 57}; +constexpr Result ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81}; +constexpr Result ERROR_INVALID_SIZE{ErrorModule::Loader, 82}; +constexpr Result ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84}; +[[maybe_unused]] constexpr Result ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85}; +constexpr Result ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87}; constexpr std::size_t MAXIMUM_LOADED_RO{0x40}; constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200}; @@ -161,7 +159,8 @@ public: class RelocatableObject final : public ServiceFramework<RelocatableObject> { public: - explicit RelocatableObject(Core::System& system_) : ServiceFramework{system_, "ldr:ro"} { + explicit RelocatableObject(Core::System& system_) + : ServiceFramework{system_, "ldr:ro", ServiceThreadType::CreateNew} { // clang-format off static const FunctionInfo functions[] = { {0, &RelocatableObject::LoadModule, "LoadModule"}, @@ -288,7 +287,7 @@ public: } bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const { - constexpr std::size_t padding_size{4 * Kernel::PageSize}; + const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize}; const auto start_info{page_table.QueryInfo(start - 1)}; if (start_info.state != Kernel::KMemoryState::Free) { @@ -308,31 +307,69 @@ public: return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize()); } - VAddr GetRandomMapRegion(const Kernel::KPageTable& page_table, std::size_t size) const { - VAddr addr{}; - const std::size_t end_pages{(page_table.GetAliasCodeRegionSize() - size) >> - Kernel::PageBits}; - do { - addr = page_table.GetAliasCodeRegionStart() + - (Kernel::KSystemControl::GenerateRandomRange(0, end_pages) << Kernel::PageBits); - } while (!page_table.IsInsideAddressSpace(addr, size) || - page_table.IsInsideHeapRegion(addr, size) || - page_table.IsInsideAliasRegion(addr, size)); - return addr; + Result GetAvailableMapRegion(Kernel::KPageTable& page_table, u64 size, VAddr& out_addr) { + size = Common::AlignUp(size, Kernel::PageSize); + size += page_table.GetNumGuardPages() * Kernel::PageSize * 4; + + const auto is_region_available = [&](VAddr addr) { + const auto end_addr = addr + size; + while (addr < end_addr) { + if (system.Memory().IsValidVirtualAddress(addr)) { + return false; + } + + if (!page_table.IsInsideAddressSpace(out_addr, size)) { + return false; + } + + if (page_table.IsInsideHeapRegion(out_addr, size)) { + return false; + } + + if (page_table.IsInsideAliasRegion(out_addr, size)) { + return false; + } + + addr += Kernel::PageSize; + } + return true; + }; + + bool succeeded = false; + const auto map_region_end = + page_table.GetAliasCodeRegionStart() + page_table.GetAliasCodeRegionSize(); + while (current_map_addr < map_region_end) { + if (is_region_available(current_map_addr)) { + succeeded = true; + break; + } + current_map_addr += 0x100000; + } + + if (!succeeded) { + ASSERT_MSG(false, "Out of address space!"); + return Kernel::ResultOutOfMemory; + } + + out_addr = current_map_addr; + current_map_addr += size; + + return ResultSuccess; } - ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr baseAddress, - u64 size) const { + ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr base_addr, u64 size) { + auto& page_table{process->PageTable()}; + VAddr addr{}; + for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { - auto& page_table{process->PageTable()}; - const VAddr addr{GetRandomMapRegion(page_table, size)}; - const ResultCode result{page_table.MapCodeMemory(addr, baseAddress, size)}; + R_TRY(GetAvailableMapRegion(page_table, size, addr)); + const Result result{page_table.MapCodeMemory(addr, base_addr, size)}; if (result == Kernel::ResultInvalidCurrentMemory) { continue; } - CASCADE_CODE(result); + R_TRY(result); if (ValidateRegionForMap(page_table, addr, size)) { return addr; @@ -343,7 +380,7 @@ public: } ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size, - VAddr bss_addr, std::size_t bss_size, std::size_t size) const { + VAddr bss_addr, std::size_t bss_size, std::size_t size) { for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { auto& page_table{process->PageTable()}; VAddr addr{}; @@ -352,12 +389,15 @@ public: if (bss_size) { auto block_guard = detail::ScopeExit([&] { - page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size); - page_table.UnmapCodeMemory(addr, nro_addr, nro_size); + page_table.UnmapCodeMemory( + addr + nro_size, bss_addr, bss_size, + Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange); + page_table.UnmapCodeMemory( + addr, nro_addr, nro_size, + Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange); }); - const ResultCode result{ - page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)}; + const Result result{page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)}; if (result == Kernel::ResultInvalidCurrentMemory) { continue; @@ -378,8 +418,8 @@ public: return ERROR_INSUFFICIENT_ADDRESS_SPACE; } - ResultCode LoadNro(Kernel::KProcess* process, const NROHeader& nro_header, VAddr nro_addr, - VAddr start) const { + Result LoadNro(Kernel::KProcess* process, const NROHeader& nro_header, VAddr nro_addr, + VAddr start) const { const VAddr text_start{start + nro_header.segment_headers[TEXT_INDEX].memory_offset}; const VAddr ro_start{start + nro_header.segment_headers[RO_INDEX].memory_offset}; const VAddr data_start{start + nro_header.segment_headers[DATA_INDEX].memory_offset}; @@ -528,22 +568,26 @@ public: rb.Push(*map_result); } - ResultCode UnmapNro(const NROInfo& info) { + Result UnmapNro(const NROInfo& info) { // Each region must be unmapped separately to validate memory state auto& page_table{system.CurrentProcess()->PageTable()}; if (info.bss_size != 0) { - CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + - info.ro_size + info.data_size, - info.bss_address, info.bss_size)); + CASCADE_CODE(page_table.UnmapCodeMemory( + info.nro_address + info.text_size + info.ro_size + info.data_size, info.bss_address, + info.bss_size, Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); } - CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size, - info.src_addr + info.text_size + info.ro_size, - info.data_size)); - CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size, - info.src_addr + info.text_size, info.ro_size)); - CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size)); + CASCADE_CODE(page_table.UnmapCodeMemory( + info.nro_address + info.text_size + info.ro_size, + info.src_addr + info.text_size + info.ro_size, info.data_size, + Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); + CASCADE_CODE(page_table.UnmapCodeMemory( + info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size, + Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); + CASCADE_CODE(page_table.UnmapCodeMemory( + info.nro_address, info.src_addr, info.text_size, + Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); return ResultSuccess; } @@ -597,6 +641,7 @@ public: LOG_WARNING(Service_LDR, "(STUBBED) called"); initialized = true; + current_map_addr = system.CurrentProcess()->PageTable().GetAliasCodeRegionStart(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -607,6 +652,7 @@ private: std::map<VAddr, NROInfo> nro; std::map<VAddr, std::vector<SHA256Hash>> nrr; + VAddr current_map_addr{}; bool IsValidNROHash(const SHA256Hash& hash) const { return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) { diff --git a/src/core/hle/service/ldr/ldr.h b/src/core/hle/service/ldr/ldr.h index 104fc15c5..25ffd8442 100644 --- a/src/core/hle/service/ldr/ldr.h +++ b/src/core/hle/service/ldr/ldr.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index e40383134..ef4b54046 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <string> diff --git a/src/core/hle/service/lm/lm.h b/src/core/hle/service/lm/lm.h index d40410b5c..266019c30 100644 --- a/src/core/hle/service/lm/lm.h +++ b/src/core/hle/service/lm/lm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/mig/mig.cpp b/src/core/hle/service/mig/mig.cpp index 1599d941b..b9fe0cecd 100644 --- a/src/core/hle/service/mig/mig.cpp +++ b/src/core/hle/service/mig/mig.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/mig/mig.h b/src/core/hle/service/mig/mig.h index 2b24cdf2c..f1641a521 100644 --- a/src/core/hle/service/mig/mig.h +++ b/src/core/hle/service/mig/mig.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index 0b907824d..390514fdc 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> @@ -13,7 +12,7 @@ namespace Service::Mii { -constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1}; +constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1}; class IDatabaseService final : public ServiceFramework<IDatabaseService> { public: @@ -44,7 +43,7 @@ public: {20, nullptr, "IsBrokenDatabaseWithClearFlag"}, {21, &IDatabaseService::GetIndex, "GetIndex"}, {22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"}, - {23, nullptr, "Convert"}, + {23, &IDatabaseService::Convert, "Convert"}, {24, nullptr, "ConvertCoreDataToCharInfo"}, {25, nullptr, "ConvertCharInfoToCoreData"}, {26, nullptr, "Append"}, @@ -131,7 +130,7 @@ private: return; } - std::vector<MiiInfo> values; + std::vector<CharInfo> values; for (const auto& element : *result) { values.emplace_back(element.info); } @@ -145,7 +144,7 @@ private: void UpdateLatest(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto info{rp.PopRaw<MiiInfo>()}; + const auto info{rp.PopRaw<CharInfo>()}; const auto source_flag{rp.PopRaw<SourceFlag>()}; LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); @@ -157,9 +156,9 @@ private: return; } - IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)}; + IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; rb.Push(ResultSuccess); - rb.PushRaw<MiiInfo>(*result); + rb.PushRaw<CharInfo>(*result); } void BuildRandom(Kernel::HLERequestContext& ctx) { @@ -192,9 +191,9 @@ private: return; } - IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)}; + IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; rb.Push(ResultSuccess); - rb.PushRaw<MiiInfo>(manager.BuildRandom(age, gender, race)); + rb.PushRaw<CharInfo>(manager.BuildRandom(age, gender, race)); } void BuildDefault(Kernel::HLERequestContext& ctx) { @@ -211,14 +210,14 @@ private: return; } - IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)}; + IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; rb.Push(ResultSuccess); - rb.PushRaw<MiiInfo>(manager.BuildDefault(index)); + rb.PushRaw<CharInfo>(manager.BuildDefault(index)); } void GetIndex(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto info{rp.PopRaw<MiiInfo>()}; + const auto info{rp.PopRaw<CharInfo>()}; LOG_DEBUG(Service_Mii, "called"); @@ -240,6 +239,18 @@ private: rb.Push(ResultSuccess); } + void Convert(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto mii_v3{rp.PopRaw<Ver3StoreData>()}; + + LOG_INFO(Service_Mii, "called"); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; + rb.Push(ResultSuccess); + rb.PushRaw<CharInfo>(manager.ConvertV3ToCharInfo(mii_v3)); + } + constexpr bool IsInterfaceVersionSupported(u32 interface_version) const { return current_interface_version >= interface_version; } diff --git a/src/core/hle/service/mii/mii.h b/src/core/hle/service/mii/mii.h index 9d3238e72..009d80d58 100644 --- a/src/core/hle/service/mii/mii.h +++ b/src/core/hle/service/mii/mii.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index 0a57c3cde..3a2fe938f 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include <random> @@ -12,13 +11,12 @@ #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/mii/raw_data.h" -#include "core/hle/service/mii/types.h" namespace Service::Mii { namespace { -constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4}; +constexpr Result ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4}; constexpr std::size_t BaseMiiCount{2}; constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; @@ -44,7 +42,7 @@ std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& i return out; } -MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) { +CharInfo ConvertStoreDataToInfo(const MiiStoreData& data) { MiiStoreBitFields bf; std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields)); @@ -292,7 +290,7 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo u8 glasses_type{}; while (glasses_type_start < glasses_type_info.values[glasses_type]) { if (++glasses_type >= glasses_type_info.values_count) { - UNREACHABLE(); + ASSERT(false); break; } } @@ -411,8 +409,8 @@ u32 MiiManager::GetCount(SourceFlag source_flag) const { return static_cast<u32>(count); } -ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info, - SourceFlag source_flag) { +ResultVal<CharInfo> MiiManager::UpdateLatest([[maybe_unused]] const CharInfo& info, + SourceFlag source_flag) { if ((source_flag & SourceFlag::Database) == SourceFlag::None) { return ERROR_CANNOT_FIND_ENTRY; } @@ -421,14 +419,242 @@ ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info return ERROR_CANNOT_FIND_ENTRY; } -MiiInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) { +CharInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) { return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id)); } -MiiInfo MiiManager::BuildDefault(std::size_t index) { +CharInfo MiiManager::BuildDefault(std::size_t index) { return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id)); } +CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const { + Service::Mii::MiiManager manager; + auto mii = manager.BuildDefault(0); + + if (!ValidateV3Info(mii_v3)) { + return mii; + } + + // TODO: We are ignoring a bunch of data from the mii_v3 + + mii.gender = static_cast<u8>(mii_v3.mii_information.gender); + mii.favorite_color = static_cast<u8>(mii_v3.mii_information.favorite_color); + mii.height = mii_v3.height; + mii.build = mii_v3.build; + + // Copy name until string terminator + mii.name = {}; + for (std::size_t index = 0; index < mii.name.size() - 1; index++) { + mii.name[index] = mii_v3.mii_name[index]; + if (mii.name[index] == 0) { + break; + } + } + + mii.font_region = mii_v3.region_information.character_set; + + mii.faceline_type = mii_v3.appearance_bits1.face_shape; + mii.faceline_color = mii_v3.appearance_bits1.skin_color; + mii.faceline_wrinkle = mii_v3.appearance_bits2.wrinkles; + mii.faceline_make = mii_v3.appearance_bits2.makeup; + + mii.hair_type = mii_v3.hair_style; + mii.hair_color = mii_v3.appearance_bits3.hair_color; + mii.hair_flip = mii_v3.appearance_bits3.flip_hair; + + mii.eye_type = static_cast<u8>(mii_v3.appearance_bits4.eye_type); + mii.eye_color = static_cast<u8>(mii_v3.appearance_bits4.eye_color); + mii.eye_scale = static_cast<u8>(mii_v3.appearance_bits4.eye_scale); + mii.eye_aspect = static_cast<u8>(mii_v3.appearance_bits4.eye_vertical_stretch); + mii.eye_rotate = static_cast<u8>(mii_v3.appearance_bits4.eye_rotation); + mii.eye_x = static_cast<u8>(mii_v3.appearance_bits4.eye_spacing); + mii.eye_y = static_cast<u8>(mii_v3.appearance_bits4.eye_y_position); + + mii.eyebrow_type = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_style); + mii.eyebrow_color = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_color); + mii.eyebrow_scale = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_scale); + mii.eyebrow_aspect = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_yscale); + mii.eyebrow_rotate = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_rotation); + mii.eyebrow_x = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_spacing); + mii.eyebrow_y = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_y_position); + + mii.nose_type = static_cast<u8>(mii_v3.appearance_bits6.nose_type); + mii.nose_scale = static_cast<u8>(mii_v3.appearance_bits6.nose_scale); + mii.nose_y = static_cast<u8>(mii_v3.appearance_bits6.nose_y_position); + + mii.mouth_type = static_cast<u8>(mii_v3.appearance_bits7.mouth_type); + mii.mouth_color = static_cast<u8>(mii_v3.appearance_bits7.mouth_color); + mii.mouth_scale = static_cast<u8>(mii_v3.appearance_bits7.mouth_scale); + mii.mouth_aspect = static_cast<u8>(mii_v3.appearance_bits7.mouth_horizontal_stretch); + mii.mouth_y = static_cast<u8>(mii_v3.appearance_bits8.mouth_y_position); + + mii.mustache_type = static_cast<u8>(mii_v3.appearance_bits8.mustache_type); + mii.mustache_scale = static_cast<u8>(mii_v3.appearance_bits9.mustache_scale); + mii.mustache_y = static_cast<u8>(mii_v3.appearance_bits9.mustache_y_position); + + mii.beard_type = static_cast<u8>(mii_v3.appearance_bits9.bear_type); + mii.beard_color = static_cast<u8>(mii_v3.appearance_bits9.facial_hair_color); + + mii.glasses_type = static_cast<u8>(mii_v3.appearance_bits10.glasses_type); + mii.glasses_color = static_cast<u8>(mii_v3.appearance_bits10.glasses_color); + mii.glasses_scale = static_cast<u8>(mii_v3.appearance_bits10.glasses_scale); + mii.glasses_y = static_cast<u8>(mii_v3.appearance_bits10.glasses_y_position); + + mii.mole_type = static_cast<u8>(mii_v3.appearance_bits11.mole_enabled); + mii.mole_scale = static_cast<u8>(mii_v3.appearance_bits11.mole_scale); + mii.mole_x = static_cast<u8>(mii_v3.appearance_bits11.mole_x_position); + mii.mole_y = static_cast<u8>(mii_v3.appearance_bits11.mole_y_position); + + // TODO: Validate mii data + + return mii; +} + +Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const { + Service::Mii::MiiManager manager; + Ver3StoreData mii_v3{}; + + // TODO: We are ignoring a bunch of data from the mii_v3 + + mii_v3.version = 1; + mii_v3.mii_information.gender.Assign(mii.gender); + mii_v3.mii_information.favorite_color.Assign(mii.favorite_color); + mii_v3.height = mii.height; + mii_v3.build = mii.build; + + // Copy name until string terminator + mii_v3.mii_name = {}; + for (std::size_t index = 0; index < mii.name.size() - 1; index++) { + mii_v3.mii_name[index] = mii.name[index]; + if (mii_v3.mii_name[index] == 0) { + break; + } + } + + mii_v3.region_information.character_set.Assign(mii.font_region); + + mii_v3.appearance_bits1.face_shape.Assign(mii.faceline_type); + mii_v3.appearance_bits1.skin_color.Assign(mii.faceline_color); + mii_v3.appearance_bits2.wrinkles.Assign(mii.faceline_wrinkle); + mii_v3.appearance_bits2.makeup.Assign(mii.faceline_make); + + mii_v3.hair_style = mii.hair_type; + mii_v3.appearance_bits3.hair_color.Assign(mii.hair_color); + mii_v3.appearance_bits3.flip_hair.Assign(mii.hair_flip); + + mii_v3.appearance_bits4.eye_type.Assign(mii.eye_type); + mii_v3.appearance_bits4.eye_color.Assign(mii.eye_color); + mii_v3.appearance_bits4.eye_scale.Assign(mii.eye_scale); + mii_v3.appearance_bits4.eye_vertical_stretch.Assign(mii.eye_aspect); + mii_v3.appearance_bits4.eye_rotation.Assign(mii.eye_rotate); + mii_v3.appearance_bits4.eye_spacing.Assign(mii.eye_x); + mii_v3.appearance_bits4.eye_y_position.Assign(mii.eye_y); + + mii_v3.appearance_bits5.eyebrow_style.Assign(mii.eyebrow_type); + mii_v3.appearance_bits5.eyebrow_color.Assign(mii.eyebrow_color); + mii_v3.appearance_bits5.eyebrow_scale.Assign(mii.eyebrow_scale); + mii_v3.appearance_bits5.eyebrow_yscale.Assign(mii.eyebrow_aspect); + mii_v3.appearance_bits5.eyebrow_rotation.Assign(mii.eyebrow_rotate); + mii_v3.appearance_bits5.eyebrow_spacing.Assign(mii.eyebrow_x); + mii_v3.appearance_bits5.eyebrow_y_position.Assign(mii.eyebrow_y); + + mii_v3.appearance_bits6.nose_type.Assign(mii.nose_type); + mii_v3.appearance_bits6.nose_scale.Assign(mii.nose_scale); + mii_v3.appearance_bits6.nose_y_position.Assign(mii.nose_y); + + mii_v3.appearance_bits7.mouth_type.Assign(mii.mouth_type); + mii_v3.appearance_bits7.mouth_color.Assign(mii.mouth_color); + mii_v3.appearance_bits7.mouth_scale.Assign(mii.mouth_scale); + mii_v3.appearance_bits7.mouth_horizontal_stretch.Assign(mii.mouth_aspect); + mii_v3.appearance_bits8.mouth_y_position.Assign(mii.mouth_y); + + mii_v3.appearance_bits8.mustache_type.Assign(mii.mustache_type); + mii_v3.appearance_bits9.mustache_scale.Assign(mii.mustache_scale); + mii_v3.appearance_bits9.mustache_y_position.Assign(mii.mustache_y); + + mii_v3.appearance_bits9.bear_type.Assign(mii.beard_type); + mii_v3.appearance_bits9.facial_hair_color.Assign(mii.beard_color); + + mii_v3.appearance_bits10.glasses_type.Assign(mii.glasses_type); + mii_v3.appearance_bits10.glasses_color.Assign(mii.glasses_color); + mii_v3.appearance_bits10.glasses_scale.Assign(mii.glasses_scale); + mii_v3.appearance_bits10.glasses_y_position.Assign(mii.glasses_y); + + mii_v3.appearance_bits11.mole_enabled.Assign(mii.mole_type); + mii_v3.appearance_bits11.mole_scale.Assign(mii.mole_scale); + mii_v3.appearance_bits11.mole_x_position.Assign(mii.mole_x); + mii_v3.appearance_bits11.mole_y_position.Assign(mii.mole_y); + + // TODO: Validate mii_v3 data + + return mii_v3; +} + +bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const { + bool is_valid = mii_v3.version == 0 || mii_v3.version == 3; + + is_valid = is_valid && (mii_v3.mii_name[0] != 0); + + is_valid = is_valid && (mii_v3.mii_information.birth_month < 13); + is_valid = is_valid && (mii_v3.mii_information.birth_day < 32); + is_valid = is_valid && (mii_v3.mii_information.favorite_color < 12); + is_valid = is_valid && (mii_v3.height < 128); + is_valid = is_valid && (mii_v3.build < 128); + + is_valid = is_valid && (mii_v3.appearance_bits1.face_shape < 12); + is_valid = is_valid && (mii_v3.appearance_bits1.skin_color < 7); + is_valid = is_valid && (mii_v3.appearance_bits2.wrinkles < 12); + is_valid = is_valid && (mii_v3.appearance_bits2.makeup < 12); + + is_valid = is_valid && (mii_v3.hair_style < 132); + is_valid = is_valid && (mii_v3.appearance_bits3.hair_color < 8); + + is_valid = is_valid && (mii_v3.appearance_bits4.eye_type < 60); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_color < 6); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_scale < 8); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_vertical_stretch < 7); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_rotation < 8); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_spacing < 13); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_y_position < 19); + + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_style < 25); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_color < 8); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_scale < 9); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_yscale < 7); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_rotation < 12); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_spacing < 12); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_y_position < 19); + + is_valid = is_valid && (mii_v3.appearance_bits6.nose_type < 18); + is_valid = is_valid && (mii_v3.appearance_bits6.nose_scale < 9); + is_valid = is_valid && (mii_v3.appearance_bits6.nose_y_position < 19); + + is_valid = is_valid && (mii_v3.appearance_bits7.mouth_type < 36); + is_valid = is_valid && (mii_v3.appearance_bits7.mouth_color < 5); + is_valid = is_valid && (mii_v3.appearance_bits7.mouth_scale < 9); + is_valid = is_valid && (mii_v3.appearance_bits7.mouth_horizontal_stretch < 7); + is_valid = is_valid && (mii_v3.appearance_bits8.mouth_y_position < 19); + + is_valid = is_valid && (mii_v3.appearance_bits8.mustache_type < 6); + is_valid = is_valid && (mii_v3.appearance_bits9.mustache_scale < 7); + is_valid = is_valid && (mii_v3.appearance_bits9.mustache_y_position < 17); + + is_valid = is_valid && (mii_v3.appearance_bits9.bear_type < 6); + is_valid = is_valid && (mii_v3.appearance_bits9.facial_hair_color < 8); + + is_valid = is_valid && (mii_v3.appearance_bits10.glasses_type < 9); + is_valid = is_valid && (mii_v3.appearance_bits10.glasses_color < 6); + is_valid = is_valid && (mii_v3.appearance_bits10.glasses_scale < 8); + is_valid = is_valid && (mii_v3.appearance_bits10.glasses_y_position < 21); + + is_valid = is_valid && (mii_v3.appearance_bits11.mole_enabled < 2); + is_valid = is_valid && (mii_v3.appearance_bits11.mole_scale < 9); + is_valid = is_valid && (mii_v3.appearance_bits11.mole_x_position < 17); + is_valid = is_valid && (mii_v3.appearance_bits11.mole_y_position < 31); + + return is_valid; +} + ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) { std::vector<MiiInfoElement> result; @@ -443,7 +669,7 @@ ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_ return result; } -ResultCode MiiManager::GetIndex([[maybe_unused]] const MiiInfo& info, u32& index) { +Result MiiManager::GetIndex([[maybe_unused]] const CharInfo& info, u32& index) { constexpr u32 INVALID_INDEX{0xFFFFFFFF}; index = INVALID_INDEX; diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h index 6999d15b1..83ad3d343 100644 --- a/src/core/hle/service/mii/mii_manager.h +++ b/src/core/hle/service/mii/mii_manager.h @@ -1,315 +1,15 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <array> #include <vector> -#include "common/bit_field.h" -#include "common/common_funcs.h" -#include "common/uuid.h" + #include "core/hle/result.h" #include "core/hle/service/mii/types.h" namespace Service::Mii { -enum class Source : u32 { - Database = 0, - Default = 1, - Account = 2, - Friend = 3, -}; - -enum class SourceFlag : u32 { - None = 0, - Database = 1 << 0, - Default = 1 << 1, -}; -DECLARE_ENUM_FLAG_OPERATORS(SourceFlag); - -struct MiiInfo { - Common::UUID uuid; - std::array<char16_t, 11> name; - u8 font_region; - u8 favorite_color; - u8 gender; - u8 height; - u8 build; - u8 type; - u8 region_move; - u8 faceline_type; - u8 faceline_color; - u8 faceline_wrinkle; - u8 faceline_make; - u8 hair_type; - u8 hair_color; - u8 hair_flip; - u8 eye_type; - u8 eye_color; - u8 eye_scale; - u8 eye_aspect; - u8 eye_rotate; - u8 eye_x; - u8 eye_y; - u8 eyebrow_type; - u8 eyebrow_color; - u8 eyebrow_scale; - u8 eyebrow_aspect; - u8 eyebrow_rotate; - u8 eyebrow_x; - u8 eyebrow_y; - u8 nose_type; - u8 nose_scale; - u8 nose_y; - u8 mouth_type; - u8 mouth_color; - u8 mouth_scale; - u8 mouth_aspect; - u8 mouth_y; - u8 beard_color; - u8 beard_type; - u8 mustache_type; - u8 mustache_scale; - u8 mustache_y; - u8 glasses_type; - u8 glasses_color; - u8 glasses_scale; - u8 glasses_y; - u8 mole_type; - u8 mole_scale; - u8 mole_x; - u8 mole_y; - u8 padding; - - std::u16string Name() const; -}; -static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size."); -static_assert(std::has_unique_object_representations_v<MiiInfo>, - "All bits of MiiInfo must contribute to its value."); - -#pragma pack(push, 4) - -struct MiiInfoElement { - MiiInfoElement(const MiiInfo& info_, Source source_) : info{info_}, source{source_} {} - - MiiInfo info{}; - Source source{}; -}; -static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size."); - -struct MiiStoreBitFields { - union { - u32 word_0{}; - - BitField<0, 8, u32> hair_type; - BitField<8, 7, u32> height; - BitField<15, 1, u32> mole_type; - BitField<16, 7, u32> build; - BitField<23, 1, HairFlip> hair_flip; - BitField<24, 7, u32> hair_color; - BitField<31, 1, u32> type; - }; - - union { - u32 word_1{}; - - BitField<0, 7, u32> eye_color; - BitField<7, 1, Gender> gender; - BitField<8, 7, u32> eyebrow_color; - BitField<16, 7, u32> mouth_color; - BitField<24, 7, u32> beard_color; - }; - - union { - u32 word_2{}; - - BitField<0, 7, u32> glasses_color; - BitField<8, 6, u32> eye_type; - BitField<14, 2, u32> region_move; - BitField<16, 6, u32> mouth_type; - BitField<22, 2, FontRegion> font_region; - BitField<24, 5, u32> eye_y; - BitField<29, 3, u32> glasses_scale; - }; - - union { - u32 word_3{}; - - BitField<0, 5, u32> eyebrow_type; - BitField<5, 3, MustacheType> mustache_type; - BitField<8, 5, u32> nose_type; - BitField<13, 3, BeardType> beard_type; - BitField<16, 5, u32> nose_y; - BitField<21, 3, u32> mouth_aspect; - BitField<24, 5, u32> mouth_y; - BitField<29, 3, u32> eyebrow_aspect; - }; - - union { - u32 word_4{}; - - BitField<0, 5, u32> mustache_y; - BitField<5, 3, u32> eye_rotate; - BitField<8, 5, u32> glasses_y; - BitField<13, 3, u32> eye_aspect; - BitField<16, 5, u32> mole_x; - BitField<21, 3, u32> eye_scale; - BitField<24, 5, u32> mole_y; - }; - - union { - u32 word_5{}; - - BitField<0, 5, u32> glasses_type; - BitField<8, 4, u32> favorite_color; - BitField<12, 4, u32> faceline_type; - BitField<16, 4, u32> faceline_color; - BitField<20, 4, u32> faceline_wrinkle; - BitField<24, 4, u32> faceline_makeup; - BitField<28, 4, u32> eye_x; - }; - - union { - u32 word_6{}; - - BitField<0, 4, u32> eyebrow_scale; - BitField<4, 4, u32> eyebrow_rotate; - BitField<8, 4, u32> eyebrow_x; - BitField<12, 4, u32> eyebrow_y; - BitField<16, 4, u32> nose_scale; - BitField<20, 4, u32> mouth_scale; - BitField<24, 4, u32> mustache_scale; - BitField<28, 4, u32> mole_scale; - }; -}; -static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size."); -static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>, - "MiiStoreBitFields is not trivially copyable."); - -struct MiiStoreData { - using Name = std::array<char16_t, 10>; - - MiiStoreData(); - MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields, - const Common::UUID& user_id); - - // This corresponds to the above structure MiiStoreBitFields. I did it like this because the - // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is - // not suitable for our uses. - struct { - std::array<u8, 0x1C> data{}; - static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size."); - - Name name{}; - Common::UUID uuid{}; - } data; - - u16 data_crc{}; - u16 device_crc{}; -}; -static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size."); - -struct MiiStoreDataElement { - MiiStoreData data{}; - Source source{}; -}; -static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size."); - -struct MiiDatabase { - u32 magic{}; // 'NFDB' - std::array<MiiStoreData, 0x64> miis{}; - INSERT_PADDING_BYTES(1); - u8 count{}; - u16 crc{}; -}; -static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size."); - -struct RandomMiiValues { - std::array<u8, 0xbc> values{}; -}; -static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size."); - -struct RandomMiiData4 { - Gender gender{}; - Age age{}; - Race race{}; - u32 values_count{}; - std::array<u32, 47> values{}; -}; -static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size."); - -struct RandomMiiData3 { - u32 arg_1; - u32 arg_2; - u32 values_count; - std::array<u32, 47> values{}; -}; -static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size."); - -struct RandomMiiData2 { - u32 arg_1; - u32 values_count; - std::array<u32, 47> values{}; -}; -static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size."); - -struct DefaultMii { - u32 face_type{}; - u32 face_color{}; - u32 face_wrinkle{}; - u32 face_makeup{}; - u32 hair_type{}; - u32 hair_color{}; - u32 hair_flip{}; - u32 eye_type{}; - u32 eye_color{}; - u32 eye_scale{}; - u32 eye_aspect{}; - u32 eye_rotate{}; - u32 eye_x{}; - u32 eye_y{}; - u32 eyebrow_type{}; - u32 eyebrow_color{}; - u32 eyebrow_scale{}; - u32 eyebrow_aspect{}; - u32 eyebrow_rotate{}; - u32 eyebrow_x{}; - u32 eyebrow_y{}; - u32 nose_type{}; - u32 nose_scale{}; - u32 nose_y{}; - u32 mouth_type{}; - u32 mouth_color{}; - u32 mouth_scale{}; - u32 mouth_aspect{}; - u32 mouth_y{}; - u32 mustache_type{}; - u32 beard_type{}; - u32 beard_color{}; - u32 mustache_scale{}; - u32 mustache_y{}; - u32 glasses_type{}; - u32 glasses_color{}; - u32 glasses_scale{}; - u32 glasses_y{}; - u32 mole_type{}; - u32 mole_scale{}; - u32 mole_x{}; - u32 mole_y{}; - u32 height{}; - u32 weight{}; - Gender gender{}; - u32 favorite_color{}; - u32 region{}; - FontRegion font_region{}; - u32 type{}; - INSERT_PADDING_WORDS(5); -}; -static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size."); - -#pragma pack(pop) - // The Mii manager is responsible for loading and storing the Miis to the database in NAND along // with providing an easy interface for HLE emulation of the mii service. class MiiManager { @@ -319,11 +19,14 @@ public: bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter); bool IsFullDatabase() const; u32 GetCount(SourceFlag source_flag) const; - ResultVal<MiiInfo> UpdateLatest(const MiiInfo& info, SourceFlag source_flag); - MiiInfo BuildRandom(Age age, Gender gender, Race race); - MiiInfo BuildDefault(std::size_t index); + ResultVal<CharInfo> UpdateLatest(const CharInfo& info, SourceFlag source_flag); + CharInfo BuildRandom(Age age, Gender gender, Race race); + CharInfo BuildDefault(std::size_t index); + CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const; + Ver3StoreData ConvertCharInfoToV3(const CharInfo& mii) const; + bool ValidateV3Info(const Ver3StoreData& mii_v3) const; ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag); - ResultCode GetIndex(const MiiInfo& info, u32& index); + Result GetIndex(const CharInfo& info, u32& index); private: const Common::UUID user_id{}; diff --git a/src/core/hle/service/mii/raw_data.cpp b/src/core/hle/service/mii/raw_data.cpp index 9d3c8017a..1442280c8 100644 --- a/src/core/hle/service/mii/raw_data.cpp +++ b/src/core/hle/service/mii/raw_data.cpp @@ -1,22 +1,5 @@ -// MIT License -// -// Copyright (c) Ryujinx Team and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -// associated documentation files (the "Software"), to deal in the Software without restriction, -// including without limitation the rights to use, copy, modify, merge, publish, distribute, -// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT -// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// +// SPDX-FileCopyrightText: Ryujinx Team and Contributors +// SPDX-License-Identifier: MIT #include "core/hle/service/mii/raw_data.h" diff --git a/src/core/hle/service/mii/raw_data.h b/src/core/hle/service/mii/raw_data.h index bd90c2162..c2bec68d4 100644 --- a/src/core/hle/service/mii/raw_data.h +++ b/src/core/hle/service/mii/raw_data.h @@ -1,12 +1,11 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <array> -#include "core/hle/service/mii/mii_manager.h" +#include "core/hle/service/mii/types.h" namespace Service::Mii::RawData { diff --git a/src/core/hle/service/mii/types.h b/src/core/hle/service/mii/types.h index d65a1055e..9e3247397 100644 --- a/src/core/hle/service/mii/types.h +++ b/src/core/hle/service/mii/types.h @@ -1,11 +1,15 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include <array> +#include <type_traits> + +#include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/uuid.h" namespace Service::Mii { @@ -25,7 +29,11 @@ enum class BeardType : u32 { Beard5, }; -enum class BeardAndMustacheFlag : u32 { Beard = 1, Mustache, All = Beard | Mustache }; +enum class BeardAndMustacheFlag : u32 { + Beard = 1, + Mustache, + All = Beard | Mustache, +}; DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag); enum class FontRegion : u32 { @@ -64,4 +72,424 @@ enum class Race : u32 { All, }; +enum class Source : u32 { + Database = 0, + Default = 1, + Account = 2, + Friend = 3, +}; + +enum class SourceFlag : u32 { + None = 0, + Database = 1 << 0, + Default = 1 << 1, +}; +DECLARE_ENUM_FLAG_OPERATORS(SourceFlag); + +// nn::mii::CharInfo +struct CharInfo { + Common::UUID uuid; + std::array<char16_t, 11> name; + u8 font_region; + u8 favorite_color; + u8 gender; + u8 height; + u8 build; + u8 type; + u8 region_move; + u8 faceline_type; + u8 faceline_color; + u8 faceline_wrinkle; + u8 faceline_make; + u8 hair_type; + u8 hair_color; + u8 hair_flip; + u8 eye_type; + u8 eye_color; + u8 eye_scale; + u8 eye_aspect; + u8 eye_rotate; + u8 eye_x; + u8 eye_y; + u8 eyebrow_type; + u8 eyebrow_color; + u8 eyebrow_scale; + u8 eyebrow_aspect; + u8 eyebrow_rotate; + u8 eyebrow_x; + u8 eyebrow_y; + u8 nose_type; + u8 nose_scale; + u8 nose_y; + u8 mouth_type; + u8 mouth_color; + u8 mouth_scale; + u8 mouth_aspect; + u8 mouth_y; + u8 beard_color; + u8 beard_type; + u8 mustache_type; + u8 mustache_scale; + u8 mustache_y; + u8 glasses_type; + u8 glasses_color; + u8 glasses_scale; + u8 glasses_y; + u8 mole_type; + u8 mole_scale; + u8 mole_x; + u8 mole_y; + u8 padding; +}; +static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size."); +static_assert(std::has_unique_object_representations_v<CharInfo>, + "All bits of CharInfo must contribute to its value."); + +#pragma pack(push, 4) + +struct MiiInfoElement { + MiiInfoElement(const CharInfo& info_, Source source_) : info{info_}, source{source_} {} + + CharInfo info{}; + Source source{}; +}; +static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size."); + +struct MiiStoreBitFields { + union { + u32 word_0{}; + + BitField<0, 8, u32> hair_type; + BitField<8, 7, u32> height; + BitField<15, 1, u32> mole_type; + BitField<16, 7, u32> build; + BitField<23, 1, HairFlip> hair_flip; + BitField<24, 7, u32> hair_color; + BitField<31, 1, u32> type; + }; + + union { + u32 word_1{}; + + BitField<0, 7, u32> eye_color; + BitField<7, 1, Gender> gender; + BitField<8, 7, u32> eyebrow_color; + BitField<16, 7, u32> mouth_color; + BitField<24, 7, u32> beard_color; + }; + + union { + u32 word_2{}; + + BitField<0, 7, u32> glasses_color; + BitField<8, 6, u32> eye_type; + BitField<14, 2, u32> region_move; + BitField<16, 6, u32> mouth_type; + BitField<22, 2, FontRegion> font_region; + BitField<24, 5, u32> eye_y; + BitField<29, 3, u32> glasses_scale; + }; + + union { + u32 word_3{}; + + BitField<0, 5, u32> eyebrow_type; + BitField<5, 3, MustacheType> mustache_type; + BitField<8, 5, u32> nose_type; + BitField<13, 3, BeardType> beard_type; + BitField<16, 5, u32> nose_y; + BitField<21, 3, u32> mouth_aspect; + BitField<24, 5, u32> mouth_y; + BitField<29, 3, u32> eyebrow_aspect; + }; + + union { + u32 word_4{}; + + BitField<0, 5, u32> mustache_y; + BitField<5, 3, u32> eye_rotate; + BitField<8, 5, u32> glasses_y; + BitField<13, 3, u32> eye_aspect; + BitField<16, 5, u32> mole_x; + BitField<21, 3, u32> eye_scale; + BitField<24, 5, u32> mole_y; + }; + + union { + u32 word_5{}; + + BitField<0, 5, u32> glasses_type; + BitField<8, 4, u32> favorite_color; + BitField<12, 4, u32> faceline_type; + BitField<16, 4, u32> faceline_color; + BitField<20, 4, u32> faceline_wrinkle; + BitField<24, 4, u32> faceline_makeup; + BitField<28, 4, u32> eye_x; + }; + + union { + u32 word_6{}; + + BitField<0, 4, u32> eyebrow_scale; + BitField<4, 4, u32> eyebrow_rotate; + BitField<8, 4, u32> eyebrow_x; + BitField<12, 4, u32> eyebrow_y; + BitField<16, 4, u32> nose_scale; + BitField<20, 4, u32> mouth_scale; + BitField<24, 4, u32> mustache_scale; + BitField<28, 4, u32> mole_scale; + }; +}; +static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size."); +static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>, + "MiiStoreBitFields is not trivially copyable."); + +// This is nn::mii::Ver3StoreData +// Based on citra HLE::Applets::MiiData and PretendoNetwork. +// https://github.com/citra-emu/citra/blob/master/src/core/hle/applets/mii_selector.h#L48 +// https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299 +struct Ver3StoreData { + u8 version; + union { + u8 raw; + + BitField<0, 1, u8> allow_copying; + BitField<1, 1, u8> profanity_flag; + BitField<2, 2, u8> region_lock; + BitField<4, 2, u8> character_set; + } region_information; + u16_be mii_id; + u64_be system_id; + u32_be specialness_and_creation_date; + std::array<u8, 0x6> creator_mac; + u16_be padding; + union { + u16 raw; + + BitField<0, 1, u16> gender; + BitField<1, 4, u16> birth_month; + BitField<5, 5, u16> birth_day; + BitField<10, 4, u16> favorite_color; + BitField<14, 1, u16> favorite; + } mii_information; + std::array<char16_t, 0xA> mii_name; + u8 height; + u8 build; + union { + u8 raw; + + BitField<0, 1, u8> disable_sharing; + BitField<1, 4, u8> face_shape; + BitField<5, 3, u8> skin_color; + } appearance_bits1; + union { + u8 raw; + + BitField<0, 4, u8> wrinkles; + BitField<4, 4, u8> makeup; + } appearance_bits2; + u8 hair_style; + union { + u8 raw; + + BitField<0, 3, u8> hair_color; + BitField<3, 1, u8> flip_hair; + } appearance_bits3; + union { + u32 raw; + + BitField<0, 6, u32> eye_type; + BitField<6, 3, u32> eye_color; + BitField<9, 4, u32> eye_scale; + BitField<13, 3, u32> eye_vertical_stretch; + BitField<16, 5, u32> eye_rotation; + BitField<21, 4, u32> eye_spacing; + BitField<25, 5, u32> eye_y_position; + } appearance_bits4; + union { + u32 raw; + + BitField<0, 5, u32> eyebrow_style; + BitField<5, 3, u32> eyebrow_color; + BitField<8, 4, u32> eyebrow_scale; + BitField<12, 3, u32> eyebrow_yscale; + BitField<16, 4, u32> eyebrow_rotation; + BitField<21, 4, u32> eyebrow_spacing; + BitField<25, 5, u32> eyebrow_y_position; + } appearance_bits5; + union { + u16 raw; + + BitField<0, 5, u16> nose_type; + BitField<5, 4, u16> nose_scale; + BitField<9, 5, u16> nose_y_position; + } appearance_bits6; + union { + u16 raw; + + BitField<0, 6, u16> mouth_type; + BitField<6, 3, u16> mouth_color; + BitField<9, 4, u16> mouth_scale; + BitField<13, 3, u16> mouth_horizontal_stretch; + } appearance_bits7; + union { + u8 raw; + + BitField<0, 5, u8> mouth_y_position; + BitField<5, 3, u8> mustache_type; + } appearance_bits8; + u8 allow_copying; + union { + u16 raw; + + BitField<0, 3, u16> bear_type; + BitField<3, 3, u16> facial_hair_color; + BitField<6, 4, u16> mustache_scale; + BitField<10, 5, u16> mustache_y_position; + } appearance_bits9; + union { + u16 raw; + + BitField<0, 4, u16> glasses_type; + BitField<4, 3, u16> glasses_color; + BitField<7, 4, u16> glasses_scale; + BitField<11, 5, u16> glasses_y_position; + } appearance_bits10; + union { + u16 raw; + + BitField<0, 1, u16> mole_enabled; + BitField<1, 4, u16> mole_scale; + BitField<5, 5, u16> mole_x_position; + BitField<10, 5, u16> mole_y_position; + } appearance_bits11; + + std::array<u16_le, 0xA> author_name; + INSERT_PADDING_BYTES(0x4); +}; +static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size"); + +struct MiiStoreData { + using Name = std::array<char16_t, 10>; + + MiiStoreData(); + MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields, + const Common::UUID& user_id); + + // This corresponds to the above structure MiiStoreBitFields. I did it like this because the + // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is + // not suitable for our uses. + struct { + std::array<u8, 0x1C> data{}; + static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size."); + + Name name{}; + Common::UUID uuid{}; + } data; + + u16 data_crc{}; + u16 device_crc{}; +}; +static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size."); + +struct MiiStoreDataElement { + MiiStoreData data{}; + Source source{}; +}; +static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size."); + +struct MiiDatabase { + u32 magic{}; // 'NFDB' + std::array<MiiStoreData, 0x64> miis{}; + INSERT_PADDING_BYTES(1); + u8 count{}; + u16 crc{}; +}; +static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size."); + +struct RandomMiiValues { + std::array<u8, 0xbc> values{}; +}; +static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size."); + +struct RandomMiiData4 { + Gender gender{}; + Age age{}; + Race race{}; + u32 values_count{}; + std::array<u32, 47> values{}; +}; +static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size."); + +struct RandomMiiData3 { + u32 arg_1; + u32 arg_2; + u32 values_count; + std::array<u32, 47> values{}; +}; +static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size."); + +struct RandomMiiData2 { + u32 arg_1; + u32 values_count; + std::array<u32, 47> values{}; +}; +static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size."); + +struct DefaultMii { + u32 face_type{}; + u32 face_color{}; + u32 face_wrinkle{}; + u32 face_makeup{}; + u32 hair_type{}; + u32 hair_color{}; + u32 hair_flip{}; + u32 eye_type{}; + u32 eye_color{}; + u32 eye_scale{}; + u32 eye_aspect{}; + u32 eye_rotate{}; + u32 eye_x{}; + u32 eye_y{}; + u32 eyebrow_type{}; + u32 eyebrow_color{}; + u32 eyebrow_scale{}; + u32 eyebrow_aspect{}; + u32 eyebrow_rotate{}; + u32 eyebrow_x{}; + u32 eyebrow_y{}; + u32 nose_type{}; + u32 nose_scale{}; + u32 nose_y{}; + u32 mouth_type{}; + u32 mouth_color{}; + u32 mouth_scale{}; + u32 mouth_aspect{}; + u32 mouth_y{}; + u32 mustache_type{}; + u32 beard_type{}; + u32 beard_color{}; + u32 mustache_scale{}; + u32 mustache_y{}; + u32 glasses_type{}; + u32 glasses_color{}; + u32 glasses_scale{}; + u32 glasses_y{}; + u32 mole_type{}; + u32 mole_scale{}; + u32 mole_x{}; + u32 mole_y{}; + u32 height{}; + u32 weight{}; + Gender gender{}; + u32 favorite_color{}; + u32 region{}; + FontRegion font_region{}; + u32 type{}; + INSERT_PADDING_WORDS(5); +}; +static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size."); + +#pragma pack(pop) + } // namespace Service::Mii diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp index 0ccfe91cd..ba8c0e230 100644 --- a/src/core/hle/service/mm/mm_u.cpp +++ b/src/core/hle/service/mm/mm_u.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" @@ -47,7 +46,7 @@ private: IPC::RequestParser rp{ctx}; min = rp.Pop<u32>(); max = rp.Pop<u32>(); - LOG_WARNING(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max); + LOG_DEBUG(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max); current = min; IPC::ResponseBuilder rb{ctx, 2}; @@ -55,7 +54,7 @@ private: } void GetOld(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_MM, "(STUBBED) called"); + LOG_DEBUG(Service_MM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); @@ -82,8 +81,8 @@ private: u32 input_id = rp.Pop<u32>(); min = rp.Pop<u32>(); max = rp.Pop<u32>(); - LOG_WARNING(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}", - input_id, min, max); + LOG_DEBUG(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}", input_id, + min, max); current = min; IPC::ResponseBuilder rb{ctx, 2}; @@ -91,7 +90,7 @@ private: } void Get(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_MM, "(STUBBED) called"); + LOG_DEBUG(Service_MM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/mm/mm_u.h b/src/core/hle/service/mm/mm_u.h index 49b6a3355..b40941e35 100644 --- a/src/core/hle/service/mm/mm_u.h +++ b/src/core/hle/service/mm/mm_u.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/mnpp/mnpp_app.cpp b/src/core/hle/service/mnpp/mnpp_app.cpp new file mode 100644 index 000000000..c3aad5714 --- /dev/null +++ b/src/core/hle/service/mnpp/mnpp_app.cpp @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/mnpp/mnpp_app.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::MNPP { + +class MNPP_APP final : public ServiceFramework<MNPP_APP> { +public: + explicit MNPP_APP(Core::System& system_) : ServiceFramework{system_, "mnpp:app"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &MNPP_APP::Unknown0, "unknown0"}, + {1, &MNPP_APP::Unknown1, "unknown1"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void Unknown0(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_MNPP, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void Unknown1(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_MNPP, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } +}; + +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { + std::make_shared<MNPP_APP>(system)->InstallAsService(service_manager); +} + +} // namespace Service::MNPP diff --git a/src/core/hle/service/mnpp/mnpp_app.h b/src/core/hle/service/mnpp/mnpp_app.h new file mode 100644 index 000000000..eec75fe0e --- /dev/null +++ b/src/core/hle/service/mnpp/mnpp_app.h @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace Core { +class System; +} + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::MNPP { + +/// Registers all MNPP services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); + +} // namespace Service::MNPP diff --git a/src/core/hle/service/ncm/ncm.cpp b/src/core/hle/service/ncm/ncm.cpp index 2dcda16f6..68210a108 100644 --- a/src/core/hle/service/ncm/ncm.cpp +++ b/src/core/hle/service/ncm/ncm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/ncm/ncm.h b/src/core/hle/service/ncm/ncm.h index ee01eddc0..de3971437 100644 --- a/src/core/hle/service/ncm/ncm.h +++ b/src/core/hle/service/ncm/ncm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index f77037842..046c5f18f 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> @@ -107,10 +106,10 @@ public: {1, &IUser::FinalizeOld, "FinalizeOld"}, {2, &IUser::GetStateOld, "GetStateOld"}, {3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"}, - {400, nullptr, "Initialize"}, - {401, nullptr, "Finalize"}, - {402, nullptr, "GetState"}, - {403, nullptr, "IsNfcEnabled"}, + {400, &IUser::InitializeOld, "Initialize"}, + {401, &IUser::FinalizeOld, "Finalize"}, + {402, &IUser::GetStateOld, "GetState"}, + {403, &IUser::IsNfcEnabledOld, "IsNfcEnabled"}, {404, nullptr, "ListDevices"}, {405, nullptr, "GetDeviceState"}, {406, nullptr, "GetNpadId"}, diff --git a/src/core/hle/service/nfc/nfc.h b/src/core/hle/service/nfc/nfc.h index 5a94b076d..0107b696c 100644 --- a/src/core/hle/service/nfc/nfc.h +++ b/src/core/hle/service/nfc/nfc.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp new file mode 100644 index 000000000..c32a6816b --- /dev/null +++ b/src/core/hle/service/nfp/amiibo_crypto.cpp @@ -0,0 +1,388 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2017 socram8888/amiitool +// SPDX-License-Identifier: MIT + +#include <array> +#include <mbedtls/aes.h> +#include <mbedtls/hmac_drbg.h> + +#include "common/fs/file.h" +#include "common/fs/path_util.h" +#include "common/logging/log.h" +#include "core/hle/service/mii/mii_manager.h" +#include "core/hle/service/nfp/amiibo_crypto.h" + +namespace Service::NFP::AmiiboCrypto { + +bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { + const auto& amiibo_data = ntag_file.user_memory; + LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock); + LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container); + LOG_DEBUG(Service_NFP, "write_count={}", static_cast<u16>(amiibo_data.write_counter)); + + LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); + LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant); + LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type); + LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", + static_cast<u16>(amiibo_data.model_info.model_number)); + LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series); + LOG_DEBUG(Service_NFP, "tag_type=0x{0:x}", amiibo_data.model_info.tag_type); + + LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock); + LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", ntag_file.CFG0); + LOG_DEBUG(Service_NFP, "tag_CFG1=0x{0:x}", ntag_file.CFG1); + + // Validate UUID + constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3` + if ((CT ^ ntag_file.uuid.uid[0] ^ ntag_file.uuid.uid[1] ^ ntag_file.uuid.uid[2]) != + ntag_file.uuid.uid[3]) { + return false; + } + if ((ntag_file.uuid.uid[4] ^ ntag_file.uuid.uid[5] ^ ntag_file.uuid.uid[6] ^ + ntag_file.uuid.nintendo_id) != ntag_file.uuid.lock_bytes[0]) { + return false; + } + + // Check against all know constants on an amiibo binary + if (ntag_file.static_lock != 0xE00F) { + return false; + } + if (ntag_file.compability_container != 0xEEFF10F1U) { + return false; + } + if (amiibo_data.constant_value != 0xA5) { + return false; + } + if (amiibo_data.model_info.tag_type != PackedTagType::Type2) { + return false; + } + if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001U) { + return false; + } + if (ntag_file.CFG0 != 0x04000000U) { + return false; + } + if (ntag_file.CFG1 != 0x5F) { + return false; + } + return true; +} + +NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { + NTAG215File encoded_data{}; + + encoded_data.uid = nfc_data.uuid.uid; + encoded_data.nintendo_id = nfc_data.uuid.nintendo_id; + encoded_data.static_lock = nfc_data.static_lock; + encoded_data.compability_container = nfc_data.compability_container; + encoded_data.hmac_data = nfc_data.user_memory.hmac_data; + encoded_data.constant_value = nfc_data.user_memory.constant_value; + encoded_data.write_counter = nfc_data.user_memory.write_counter; + encoded_data.settings = nfc_data.user_memory.settings; + encoded_data.owner_mii = nfc_data.user_memory.owner_mii; + encoded_data.title_id = nfc_data.user_memory.title_id; + encoded_data.applicaton_write_counter = nfc_data.user_memory.applicaton_write_counter; + encoded_data.application_area_id = nfc_data.user_memory.application_area_id; + encoded_data.unknown = nfc_data.user_memory.unknown; + encoded_data.unknown2 = nfc_data.user_memory.unknown2; + encoded_data.application_area = nfc_data.user_memory.application_area; + encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag; + encoded_data.lock_bytes = nfc_data.uuid.lock_bytes; + encoded_data.model_info = nfc_data.user_memory.model_info; + encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt; + encoded_data.dynamic_lock = nfc_data.dynamic_lock; + encoded_data.CFG0 = nfc_data.CFG0; + encoded_data.CFG1 = nfc_data.CFG1; + encoded_data.password = nfc_data.password; + + return encoded_data; +} + +EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { + EncryptedNTAG215File nfc_data{}; + + nfc_data.uuid.uid = encoded_data.uid; + nfc_data.uuid.nintendo_id = encoded_data.nintendo_id; + nfc_data.uuid.lock_bytes = encoded_data.lock_bytes; + nfc_data.static_lock = encoded_data.static_lock; + nfc_data.compability_container = encoded_data.compability_container; + nfc_data.user_memory.hmac_data = encoded_data.hmac_data; + nfc_data.user_memory.constant_value = encoded_data.constant_value; + nfc_data.user_memory.write_counter = encoded_data.write_counter; + nfc_data.user_memory.settings = encoded_data.settings; + nfc_data.user_memory.owner_mii = encoded_data.owner_mii; + nfc_data.user_memory.title_id = encoded_data.title_id; + nfc_data.user_memory.applicaton_write_counter = encoded_data.applicaton_write_counter; + nfc_data.user_memory.application_area_id = encoded_data.application_area_id; + nfc_data.user_memory.unknown = encoded_data.unknown; + nfc_data.user_memory.unknown2 = encoded_data.unknown2; + nfc_data.user_memory.application_area = encoded_data.application_area; + nfc_data.user_memory.hmac_tag = encoded_data.hmac_tag; + nfc_data.user_memory.model_info = encoded_data.model_info; + nfc_data.user_memory.keygen_salt = encoded_data.keygen_salt; + nfc_data.dynamic_lock = encoded_data.dynamic_lock; + nfc_data.CFG0 = encoded_data.CFG0; + nfc_data.CFG1 = encoded_data.CFG1; + nfc_data.password = encoded_data.password; + + return nfc_data; +} + +u32 GetTagPassword(const TagUuid& uuid) { + // Verifiy that the generated password is correct + u32 password = 0xAA ^ (uuid.uid[1] ^ uuid.uid[3]); + password &= (0x55 ^ (uuid.uid[2] ^ uuid.uid[4])) << 8; + password &= (0xAA ^ (uuid.uid[3] ^ uuid.uid[5])) << 16; + password &= (0x55 ^ (uuid.uid[4] ^ uuid.uid[6])) << 24; + return password; +} + +HashSeed GetSeed(const NTAG215File& data) { + HashSeed seed{ + .magic = data.write_counter, + .padding = {}, + .uid_1 = data.uid, + .nintendo_id_1 = data.nintendo_id, + .uid_2 = data.uid, + .nintendo_id_2 = data.nintendo_id, + .keygen_salt = data.keygen_salt, + }; + + return seed; +} + +std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed) { + const std::size_t seedPart1Len = sizeof(key.magic_bytes) - key.magic_length; + const std::size_t string_size = key.type_string.size(); + std::vector<u8> output(string_size + seedPart1Len); + + // Copy whole type string + memccpy(output.data(), key.type_string.data(), '\0', string_size); + + // Append (16 - magic_length) from the input seed + memcpy(output.data() + string_size, &seed, seedPart1Len); + + // Append all bytes from magicBytes + output.insert(output.end(), key.magic_bytes.begin(), + key.magic_bytes.begin() + key.magic_length); + + output.insert(output.end(), seed.uid_1.begin(), seed.uid_1.end()); + output.emplace_back(seed.nintendo_id_1); + output.insert(output.end(), seed.uid_2.begin(), seed.uid_2.end()); + output.emplace_back(seed.nintendo_id_2); + + for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) { + output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i])); + } + + return output; +} + +void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, + const std::vector<u8>& seed) { + // Initialize context + ctx.used = false; + ctx.counter = 0; + ctx.buffer_size = sizeof(ctx.counter) + seed.size(); + memcpy(ctx.buffer.data() + sizeof(u16), seed.data(), seed.size()); + + // Initialize HMAC context + mbedtls_md_init(&hmac_ctx); + mbedtls_md_setup(&hmac_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1); + mbedtls_md_hmac_starts(&hmac_ctx, hmac_key.data(), hmac_key.size()); +} + +void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output) { + // If used at least once, reinitialize the HMAC + if (ctx.used) { + mbedtls_md_hmac_reset(&hmac_ctx); + } + + ctx.used = true; + + // Store counter in big endian, and increment it + ctx.buffer[0] = static_cast<u8>(ctx.counter >> 8); + ctx.buffer[1] = static_cast<u8>(ctx.counter >> 0); + ctx.counter++; + + // Do HMAC magic + mbedtls_md_hmac_update(&hmac_ctx, reinterpret_cast<const unsigned char*>(ctx.buffer.data()), + ctx.buffer_size); + mbedtls_md_hmac_finish(&hmac_ctx, output.data()); +} + +DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data) { + const auto seed = GetSeed(data); + + // Generate internal seed + const std::vector<u8> internal_key = GenerateInternalKey(key, seed); + + // Initialize context + CryptoCtx ctx{}; + mbedtls_md_context_t hmac_ctx; + CryptoInit(ctx, hmac_ctx, key.hmac_key, internal_key); + + // Generate derived keys + DerivedKeys derived_keys{}; + std::array<DrgbOutput, 2> temp{}; + CryptoStep(ctx, hmac_ctx, temp[0]); + CryptoStep(ctx, hmac_ctx, temp[1]); + memcpy(&derived_keys, temp.data(), sizeof(DerivedKeys)); + + // Cleanup context + mbedtls_md_free(&hmac_ctx); + + return derived_keys; +} + +void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& out_data) { + mbedtls_aes_context aes; + std::size_t nc_off = 0; + std::array<u8, sizeof(keys.aes_iv)> nonce_counter{}; + std::array<u8, sizeof(keys.aes_iv)> stream_block{}; + + const auto aes_key_size = static_cast<u32>(keys.aes_key.size() * 8); + mbedtls_aes_setkey_enc(&aes, keys.aes_key.data(), aes_key_size); + memcpy(nonce_counter.data(), keys.aes_iv.data(), sizeof(keys.aes_iv)); + + constexpr std::size_t encrypted_data_size = HMAC_TAG_START - SETTINGS_START; + mbedtls_aes_crypt_ctr(&aes, encrypted_data_size, &nc_off, nonce_counter.data(), + stream_block.data(), + reinterpret_cast<const unsigned char*>(&in_data.settings), + reinterpret_cast<unsigned char*>(&out_data.settings)); + + // Copy the rest of the data directly + out_data.uid = in_data.uid; + out_data.nintendo_id = in_data.nintendo_id; + out_data.lock_bytes = in_data.lock_bytes; + out_data.static_lock = in_data.static_lock; + out_data.compability_container = in_data.compability_container; + + out_data.constant_value = in_data.constant_value; + out_data.write_counter = in_data.write_counter; + + out_data.model_info = in_data.model_info; + out_data.keygen_salt = in_data.keygen_salt; + out_data.dynamic_lock = in_data.dynamic_lock; + out_data.CFG0 = in_data.CFG0; + out_data.CFG1 = in_data.CFG1; + out_data.password = in_data.password; +} + +bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) { + const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); + + const Common::FS::IOFile keys_file{yuzu_keys_dir / "key_retail.bin", + Common::FS::FileAccessMode::Read, + Common::FS::FileType::BinaryFile}; + + if (!keys_file.IsOpen()) { + LOG_ERROR(Service_NFP, "No keys detected"); + return false; + } + + if (keys_file.Read(unfixed_info) != 1) { + LOG_ERROR(Service_NFP, "Failed to read unfixed_info"); + return false; + } + if (keys_file.Read(locked_secret) != 1) { + LOG_ERROR(Service_NFP, "Failed to read locked-secret"); + return false; + } + + return true; +} + +bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) { + InternalKey locked_secret{}; + InternalKey unfixed_info{}; + + if (!LoadKeys(locked_secret, unfixed_info)) { + return false; + } + + // Generate keys + NTAG215File encoded_data = NfcDataToEncodedData(encrypted_tag_data); + const auto data_keys = GenerateKey(unfixed_info, encoded_data); + const auto tag_keys = GenerateKey(locked_secret, encoded_data); + + // Decrypt + Cipher(data_keys, encoded_data, tag_data); + + // Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC! + constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), + sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uid), + input_length, reinterpret_cast<unsigned char*>(&tag_data.hmac_tag)); + + // Regenerate data HMAC + constexpr std::size_t input_length2 = DYNAMIC_LOCK_START - WRITE_COUNTER_START; + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), data_keys.hmac_key.data(), + sizeof(HmacKey), + reinterpret_cast<const unsigned char*>(&tag_data.write_counter), input_length2, + reinterpret_cast<unsigned char*>(&tag_data.hmac_data)); + + if (tag_data.hmac_data != encrypted_tag_data.user_memory.hmac_data) { + LOG_ERROR(Service_NFP, "hmac_data doesn't match"); + return false; + } + + if (tag_data.hmac_tag != encrypted_tag_data.user_memory.hmac_tag) { + LOG_ERROR(Service_NFP, "hmac_tag doesn't match"); + return false; + } + + return true; +} + +bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_tag_data) { + InternalKey locked_secret{}; + InternalKey unfixed_info{}; + + if (!LoadKeys(locked_secret, unfixed_info)) { + return false; + } + + // Generate keys + const auto data_keys = GenerateKey(unfixed_info, tag_data); + const auto tag_keys = GenerateKey(locked_secret, tag_data); + + NTAG215File encoded_tag_data{}; + + // Generate tag HMAC + constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; + constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START; + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), + sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uid), + input_length, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag)); + + // Init mbedtls HMAC context + mbedtls_md_context_t ctx; + mbedtls_md_init(&ctx); + mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1); + + // Generate data HMAC + mbedtls_md_hmac_starts(&ctx, data_keys.hmac_key.data(), sizeof(HmacKey)); + mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.write_counter), + input_length2); // Data + mbedtls_md_hmac_update(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag), + sizeof(HashData)); // Tag HMAC + mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.uid), + input_length); + mbedtls_md_hmac_finish(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_data)); + + // HMAC cleanup + mbedtls_md_free(&ctx); + + // Encrypt + Cipher(data_keys, tag_data, encoded_tag_data); + + // Convert back to hardware + encrypted_tag_data = EncodedDataToNfcData(encoded_tag_data); + + return true; +} + +} // namespace Service::NFP::AmiiboCrypto diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h new file mode 100644 index 000000000..0175ced91 --- /dev/null +++ b/src/core/hle/service/nfp/amiibo_crypto.h @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <array> + +#include "core/hle/service/nfp/nfp_types.h" + +struct mbedtls_md_context_t; + +namespace Service::NFP::AmiiboCrypto { +// Byte locations in Service::NFP::NTAG215File +constexpr std::size_t HMAC_DATA_START = 0x8; +constexpr std::size_t SETTINGS_START = 0x2c; +constexpr std::size_t WRITE_COUNTER_START = 0x29; +constexpr std::size_t HMAC_TAG_START = 0x1B4; +constexpr std::size_t UUID_START = 0x1D4; +constexpr std::size_t DYNAMIC_LOCK_START = 0x208; + +using HmacKey = std::array<u8, 0x10>; +using DrgbOutput = std::array<u8, 0x20>; + +struct HashSeed { + u16_be magic; + std::array<u8, 0xE> padding; + UniqueSerialNumber uid_1; + u8 nintendo_id_1; + UniqueSerialNumber uid_2; + u8 nintendo_id_2; + std::array<u8, 0x20> keygen_salt; +}; +static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size"); + +struct InternalKey { + HmacKey hmac_key; + std::array<char, 0xE> type_string; + u8 reserved; + u8 magic_length; + std::array<u8, 0x10> magic_bytes; + std::array<u8, 0x20> xor_pad; +}; +static_assert(sizeof(InternalKey) == 0x50, "InternalKey is an invalid size"); +static_assert(std::is_trivially_copyable_v<InternalKey>, "InternalKey must be trivially copyable."); + +struct CryptoCtx { + std::array<char, 480> buffer; + bool used; + std::size_t buffer_size; + s16 counter; +}; + +struct DerivedKeys { + std::array<u8, 0x10> aes_key; + std::array<u8, 0x10> aes_iv; + std::array<u8, 0x10> hmac_key; +}; +static_assert(sizeof(DerivedKeys) == 0x30, "DerivedKeys is an invalid size"); + +/// Validates that the amiibo file is not corrupted +bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file); + +/// Converts from encrypted file format to encoded file format +NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data); + +/// Converts from encoded file format to encrypted file format +EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data); + +/// Returns password needed to allow write access to protected memory +u32 GetTagPassword(const TagUuid& uuid); + +// Generates Seed needed for key derivation +HashSeed GetSeed(const NTAG215File& data); + +// Middle step on the generation of derived keys +std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed); + +// Initializes mbedtls context +void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, + const std::vector<u8>& seed); + +// Feeds data to mbedtls context to generate the derived key +void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output); + +// Generates the derived key from amiibo data +DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data); + +// Encodes or decodes amiibo data +void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& out_data); + +/// Loads both amiibo keys from key_retail.bin +bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info); + +/// Decodes encripted amiibo data returns true if output is valid +bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data); + +/// Encodes plain amiibo data returns true if output is valid +bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_tag_data); + +} // namespace Service::NFP::AmiiboCrypto diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 761d0d3c6..0cb55ca49 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -1,361 +1,43 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <array> -#include <atomic> +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" -#include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/k_event.h" #include "core/hle/service/nfp/nfp.h" #include "core/hle/service/nfp/nfp_user.h" namespace Service::NFP { -namespace ErrCodes { -constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); -} // namespace ErrCodes - -Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, - const char* name) - : ServiceFramework{system_, name}, module{std::move(module_)}, service_context{system_, - "NFP::IUser"} { - nfc_tag_load = service_context.CreateEvent("NFP::IUser:NFCTagDetected"); -} - -Module::Interface::~Interface() { - service_context.CloseEvent(nfc_tag_load); -} -class IUser final : public ServiceFramework<IUser> { +class IUserManager final : public ServiceFramework<IUserManager> { public: - explicit IUser(Module::Interface& nfp_interface_, Core::System& system_, - KernelHelpers::ServiceContext& service_context_) - : ServiceFramework{system_, "NFP::IUser"}, nfp_interface{nfp_interface_}, - service_context{service_context_} { + explicit IUserManager(Core::System& system_) : ServiceFramework{system_, "nfp:user"} { + // clang-format off static const FunctionInfo functions[] = { - {0, &IUser::Initialize, "Initialize"}, - {1, &IUser::Finalize, "Finalize"}, - {2, &IUser::ListDevices, "ListDevices"}, - {3, &IUser::StartDetection, "StartDetection"}, - {4, &IUser::StopDetection, "StopDetection"}, - {5, &IUser::Mount, "Mount"}, - {6, &IUser::Unmount, "Unmount"}, - {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, - {8, &IUser::GetApplicationArea, "GetApplicationArea"}, - {9, nullptr, "SetApplicationArea"}, - {10, nullptr, "Flush"}, - {11, nullptr, "Restore"}, - {12, nullptr, "CreateApplicationArea"}, - {13, &IUser::GetTagInfo, "GetTagInfo"}, - {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, - {15, &IUser::GetCommonInfo, "GetCommonInfo"}, - {16, &IUser::GetModelInfo, "GetModelInfo"}, - {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, - {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, - {19, &IUser::GetState, "GetState"}, - {20, &IUser::GetDeviceState, "GetDeviceState"}, - {21, &IUser::GetNpadId, "GetNpadId"}, - {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, - {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, - {24, nullptr, "RecreateApplicationArea"}, + {0, &IUserManager::CreateUserInterface, "CreateUserInterface"}, }; - RegisterHandlers(functions); - - deactivate_event = service_context.CreateEvent("NFP::IUser:DeactivateEvent"); - availability_change_event = - service_context.CreateEvent("NFP::IUser:AvailabilityChangeEvent"); - } + // clang-format on - ~IUser() override { - service_context.CloseEvent(deactivate_event); - service_context.CloseEvent(availability_change_event); + RegisterHandlers(functions); } private: - struct TagInfo { - std::array<u8, 10> uuid; - u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it - // mean something else - std::array<u8, 0x15> padding_1; - u32_le protocol; - u32_le tag_type; - std::array<u8, 0x2c> padding_2; - }; - static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size"); - - enum class State : u32 { - NonInitialized = 0, - Initialized = 1, - }; - - enum class DeviceState : u32 { - Initialized = 0, - SearchingForTag = 1, - TagFound = 2, - TagRemoved = 3, - TagNearby = 4, - Unknown5 = 5, - Finalized = 6 - }; - - struct CommonInfo { - u16_be last_write_year; - u8 last_write_month; - u8 last_write_day; - u16_be write_counter; - u16_be version; - u32_be application_area_size; - INSERT_PADDING_BYTES(0x34); - }; - static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); - - void Initialize(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFC, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0}; - rb.Push(ResultSuccess); - - state = State::Initialized; - } - - void GetState(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFC, "called"); - - IPC::ResponseBuilder rb{ctx, 3, 0}; - rb.Push(ResultSuccess); - rb.PushRaw<u32>(static_cast<u32>(state)); - } - - void ListDevices(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 array_size = rp.Pop<u32>(); - LOG_DEBUG(Service_NFP, "called, array_size={}", array_size); - - ctx.WriteBuffer(device_handle); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(1); - } - - void GetNpadId(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 dev_handle = rp.Pop<u64>(); - LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(npad_id); - } - - void AttachActivateEvent(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 dev_handle = rp.Pop<u64>(); - LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(nfp_interface.GetNFCEvent()); - has_attached_handle = true; - } - - void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 dev_handle = rp.Pop<u64>(); - LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(deactivate_event->GetReadableEvent()); - } - - void StopDetection(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - switch (device_state) { - case DeviceState::TagFound: - case DeviceState::TagNearby: - deactivate_event->GetWritableEvent().Signal(); - device_state = DeviceState::Initialized; - break; - case DeviceState::SearchingForTag: - case DeviceState::TagRemoved: - device_state = DeviceState::Initialized; - break; - default: - break; - } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetDeviceState(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(static_cast<u32>(device_state)); - } - - void StartDetection(Kernel::HLERequestContext& ctx) { + void CreateUserInterface(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NFP, "called"); - if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { - device_state = DeviceState::SearchingForTag; + if (user_interface == nullptr) { + user_interface = std::make_shared<IUser>(system); } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetTagInfo(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - IPC::ResponseBuilder rb{ctx, 2}; - const auto& amiibo = nfp_interface.GetAmiiboBuffer(); - const TagInfo tag_info{ - .uuid = amiibo.uuid, - .uuid_length = static_cast<u8>(amiibo.uuid.size()), - .padding_1 = {}, - .protocol = 1, // TODO(ogniK): Figure out actual values - .tag_type = 2, - .padding_2 = {}, - }; - ctx.WriteBuffer(tag_info); - rb.Push(ResultSuccess); - } - - void Mount(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - device_state = DeviceState::TagNearby; - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetModelInfo(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - IPC::ResponseBuilder rb{ctx, 2}; - const auto& amiibo = nfp_interface.GetAmiiboBuffer(); - ctx.WriteBuffer(amiibo.model_info); - rb.Push(ResultSuccess); - } - - void Unmount(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - device_state = DeviceState::TagFound; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void Finalize(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - device_state = DeviceState::Finalized; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(availability_change_event->GetReadableEvent()); - } - - void GetRegisterInfo(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); - - // TODO(ogniK): Pull Mii and owner data from amiibo - IPC::ResponseBuilder rb{ctx, 2}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); + rb.PushIpcInterface<IUser>(user_interface); } - void GetCommonInfo(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); - - // TODO(ogniK): Pull common information from amiibo - - CommonInfo common_info{}; - common_info.application_area_size = 0; - ctx.WriteBuffer(common_info); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void OpenApplicationArea(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::ERR_NO_APPLICATION_AREA); - } - - void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); - // We don't need to worry about this since we can just open the file - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub - } - - void GetApplicationArea(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); - - // TODO(ogniK): Pull application area from amiibo - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub - } - - Module::Interface& nfp_interface; - KernelHelpers::ServiceContext& service_context; - - bool has_attached_handle{}; - const u64 device_handle{0}; // Npad device 1 - const u32 npad_id{0}; // Player 1 controller - State state{State::NonInitialized}; - DeviceState device_state{DeviceState::Initialized}; - Kernel::KEvent* deactivate_event; - Kernel::KEvent* availability_change_event; + std::shared_ptr<IUser> user_interface; }; -void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IUser>(*this, system, service_context); -} - -bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { - if (buffer.size() < sizeof(AmiiboFile)) { - return false; - } - - std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); - nfc_tag_load->GetWritableEvent().Signal(); - return true; -} - -Kernel::KReadableEvent& Module::Interface::GetNFCEvent() { - return nfc_tag_load->GetReadableEvent(); -} - -const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const { - return amiibo; -} - void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { - auto module = std::make_shared<Module>(); - std::make_shared<NFP_User>(module, system)->InstallAsService(service_manager); + std::make_shared<IUserManager>(system)->InstallAsService(service_manager); } } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 95c127efb..a25c362b8 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h @@ -1,57 +1,12 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <array> -#include <vector> - -#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" -namespace Kernel { -class KEvent; -} - namespace Service::NFP { -class Module final { -public: - class Interface : public ServiceFramework<Interface> { - public: - explicit Interface(std::shared_ptr<Module> module_, Core::System& system_, - const char* name); - ~Interface() override; - - struct ModelInfo { - std::array<u8, 0x8> amiibo_identification_block; - INSERT_PADDING_BYTES(0x38); - }; - static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); - - struct AmiiboFile { - std::array<u8, 10> uuid; - INSERT_PADDING_BYTES(0x4a); - ModelInfo model_info; - }; - static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size"); - - void CreateUserInterface(Kernel::HLERequestContext& ctx); - bool LoadAmiibo(const std::vector<u8>& buffer); - Kernel::KReadableEvent& GetNFCEvent(); - const AmiiboFile& GetAmiiboBuffer() const; - - protected: - std::shared_ptr<Module> module; - - private: - KernelHelpers::ServiceContext service_context; - Kernel::KEvent* nfc_tag_load; - AmiiboFile amiibo{}; - }; -}; - void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp new file mode 100644 index 000000000..ec895ac01 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_device.cpp @@ -0,0 +1,681 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <array> +#include <atomic> + +#include "common/fs/file.h" +#include "common/fs/path_util.h" +#include "common/input.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "common/tiny_mt.h" +#include "core/core.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hid/hid_types.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/mii/mii_manager.h" +#include "core/hle/service/nfp/amiibo_crypto.h" +#include "core/hle/service/nfp/nfp.h" +#include "core/hle/service/nfp/nfp_device.h" +#include "core/hle/service/nfp/nfp_result.h" +#include "core/hle/service/nfp/nfp_user.h" +#include "core/hle/service/time/time_manager.h" +#include "core/hle/service/time/time_zone_content_manager.h" +#include "core/hle/service/time/time_zone_types.h" + +namespace Service::NFP { +NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, + KernelHelpers::ServiceContext& service_context_, + Kernel::KEvent* availability_change_event_) + : npad_id{npad_id_}, system{system_}, service_context{service_context_}, + availability_change_event{availability_change_event_} { + activate_event = service_context.CreateEvent("IUser:NFPActivateEvent"); + deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent"); + npad_device = system.HIDCore().GetEmulatedController(npad_id); + + Core::HID::ControllerUpdateCallback engine_callback{ + .on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); }, + .is_npad_service = false, + }; + is_controller_set = true; + callback_key = npad_device->SetCallback(engine_callback); + + auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; + current_posix_time = standard_steady_clock.GetCurrentTimePoint(system).time_point; +} + +NfpDevice::~NfpDevice() { + if (!is_controller_set) { + return; + } + npad_device->DeleteCallback(callback_key); + is_controller_set = false; +}; + +void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { + if (type == Core::HID::ControllerTriggerType::Connected || + type == Core::HID::ControllerTriggerType::Disconnected) { + availability_change_event->GetWritableEvent().Signal(); + return; + } + + if (type != Core::HID::ControllerTriggerType::Nfc) { + return; + } + + if (!npad_device->IsConnected()) { + return; + } + + const auto nfc_status = npad_device->GetNfc(); + switch (nfc_status.state) { + case Common::Input::NfcState::NewAmiibo: + LoadAmiibo(nfc_status.data); + break; + case Common::Input::NfcState::AmiiboRemoved: + if (device_state != DeviceState::SearchingForTag) { + CloseAmiibo(); + } + break; + default: + break; + } +} + +bool NfpDevice::LoadAmiibo(std::span<const u8> data) { + if (device_state != DeviceState::SearchingForTag) { + LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state); + return false; + } + + if (data.size() != sizeof(EncryptedNTAG215File)) { + LOG_ERROR(Service_NFP, "Not an amiibo, size={}", data.size()); + return false; + } + + memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File)); + + device_state = DeviceState::TagFound; + deactivate_event->GetReadableEvent().Clear(); + activate_event->GetWritableEvent().Signal(); + return true; +} + +void NfpDevice::CloseAmiibo() { + LOG_INFO(Service_NFP, "Remove amiibo"); + + if (device_state == DeviceState::TagMounted) { + Unmount(); + } + + device_state = DeviceState::TagRemoved; + encrypted_tag_data = {}; + tag_data = {}; + activate_event->GetReadableEvent().Clear(); + deactivate_event->GetWritableEvent().Signal(); +} + +Kernel::KReadableEvent& NfpDevice::GetActivateEvent() const { + return activate_event->GetReadableEvent(); +} + +Kernel::KReadableEvent& NfpDevice::GetDeactivateEvent() const { + return deactivate_event->GetReadableEvent(); +} + +void NfpDevice::Initialize() { + device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable; + encrypted_tag_data = {}; + tag_data = {}; +} + +void NfpDevice::Finalize() { + if (device_state == DeviceState::TagMounted) { + Unmount(); + } + if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { + StopDetection(); + } + device_state = DeviceState::Unavailable; +} + +Result NfpDevice::StartDetection(s32 protocol_) { + if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return WrongDeviceState; + } + + if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) { + LOG_ERROR(Service_NFP, "Nfc not supported"); + return NfcDisabled; + } + + device_state = DeviceState::SearchingForTag; + protocol = protocol_; + return ResultSuccess; +} + +Result NfpDevice::StopDetection() { + npad_device->SetPollingMode(Common::Input::PollingMode::Active); + + if (device_state == DeviceState::Initialized) { + return ResultSuccess; + } + + if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { + CloseAmiibo(); + return ResultSuccess; + } + if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { + device_state = DeviceState::Initialized; + return ResultSuccess; + } + + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return WrongDeviceState; +} + +Result NfpDevice::Flush() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + auto& settings = tag_data.settings; + + const auto& current_date = GetAmiiboDate(current_posix_time); + if (settings.write_date.raw_date != current_date.raw_date) { + settings.write_date = current_date; + settings.crc_counter++; + // TODO: Find how to calculate the crc check + // settings.crc = CalculateCRC(settings); + } + + tag_data.write_counter++; + + if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) { + LOG_ERROR(Service_NFP, "Failed to encode data"); + return WriteAmiiboFailed; + } + + std::vector<u8> data(sizeof(encrypted_tag_data)); + memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); + + if (!npad_device->WriteNfc(data)) { + LOG_ERROR(Service_NFP, "Error writing to file"); + return WriteAmiiboFailed; + } + + is_data_moddified = false; + + return ResultSuccess; +} + +Result NfpDevice::Mount(MountTarget mount_target_) { + if (device_state != DeviceState::TagFound) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return WrongDeviceState; + } + + if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { + LOG_ERROR(Service_NFP, "Not an amiibo"); + return NotAnAmiibo; + } + + if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { + LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); + return CorruptedData; + } + + device_state = DeviceState::TagMounted; + mount_target = mount_target_; + return ResultSuccess; +} + +Result NfpDevice::Unmount() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + // Save data before unloading the amiibo + if (is_data_moddified) { + Flush(); + } + + device_state = DeviceState::TagFound; + mount_target = MountTarget::None; + is_app_area_open = false; + + return ResultSuccess; +} + +Result NfpDevice::GetTagInfo(TagInfo& tag_info) const { + if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + tag_info = { + .uuid = encrypted_tag_data.uuid.uid, + .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()), + .protocol = TagProtocol::TypeA, + .tag_type = TagType::Type2, + }; + + return ResultSuccess; +} + +Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + const auto& settings = tag_data.settings; + + // TODO: Validate this data + common_info = { + .last_write_date = settings.write_date.GetWriteDate(), + .write_counter = tag_data.write_counter, + .version = 0, + .application_area_size = sizeof(ApplicationArea), + }; + return ResultSuccess; +} + +Result NfpDevice::GetModelInfo(ModelInfo& model_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + const auto& model_info_data = encrypted_tag_data.user_memory.model_info; + model_info = { + .character_id = model_info_data.character_id, + .character_variant = model_info_data.character_variant, + .amiibo_type = model_info_data.amiibo_type, + .model_number = model_info_data.model_number, + .series = model_info_data.series, + }; + return ResultSuccess; +} + +Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (tag_data.settings.settings.amiibo_initialized == 0) { + return RegistrationIsNotInitialized; + } + + Service::Mii::MiiManager manager; + const auto& settings = tag_data.settings; + + // TODO: Validate this data + register_info = { + .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), + .creation_date = settings.init_date.GetWriteDate(), + .amiibo_name = GetAmiiboName(settings), + .font_region = {}, + }; + + return ResultSuccess; +} + +Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + Service::Mii::MiiManager manager; + auto& settings = tag_data.settings; + + settings.init_date = GetAmiiboDate(current_posix_time); + settings.write_date = GetAmiiboDate(current_posix_time); + settings.crc_counter++; + // TODO: Find how to calculate the crc check + // settings.crc = CalculateCRC(settings); + + SetAmiiboName(settings, amiibo_name); + tag_data.owner_mii = manager.ConvertCharInfoToV3(manager.BuildDefault(0)); + settings.settings.amiibo_initialized.Assign(1); + + return Flush(); +} + +Result NfpDevice::RestoreAmiibo() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + // TODO: Load amiibo from backup on system + LOG_ERROR(Service_NFP, "Not Implemented"); + return ResultSuccess; +} + +Result NfpDevice::DeleteAllData() { + const auto result = DeleteApplicationArea(); + if (result.IsError()) { + return result; + } + + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + Common::TinyMT rng{}; + rng.GenerateRandomBytes(&tag_data.owner_mii, sizeof(tag_data.owner_mii)); + tag_data.settings.settings.amiibo_initialized.Assign(0); + + return Flush(); +} + +Result NfpDevice::OpenApplicationArea(u32 access_id) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { + LOG_WARNING(Service_NFP, "Application area is not initialized"); + return ApplicationAreaIsNotInitialized; + } + + if (tag_data.application_area_id != access_id) { + LOG_WARNING(Service_NFP, "Wrong application area id"); + return WrongApplicationAreaId; + } + + is_app_area_open = true; + + return ResultSuccess; +} + +Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (!is_app_area_open) { + LOG_ERROR(Service_NFP, "Application area is not open"); + return WrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { + LOG_ERROR(Service_NFP, "Application area is not initialized"); + return ApplicationAreaIsNotInitialized; + } + + if (data.size() > sizeof(ApplicationArea)) { + data.resize(sizeof(ApplicationArea)); + } + + memcpy(data.data(), tag_data.application_area.data(), data.size()); + + return ResultSuccess; +} + +Result NfpDevice::SetApplicationArea(std::span<const u8> data) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (!is_app_area_open) { + LOG_ERROR(Service_NFP, "Application area is not open"); + return WrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { + LOG_ERROR(Service_NFP, "Application area is not initialized"); + return ApplicationAreaIsNotInitialized; + } + + if (data.size() > sizeof(ApplicationArea)) { + LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); + return ResultUnknown; + } + + Common::TinyMT rng{}; + std::memcpy(tag_data.application_area.data(), data.data(), data.size()); + // Fill remaining data with random numbers + rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), + sizeof(ApplicationArea) - data.size()); + + tag_data.applicaton_write_counter++; + is_data_moddified = true; + + return ResultSuccess; +} + +Result NfpDevice::CreateApplicationArea(u32 access_id, std::span<const u8> data) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() != 0) { + LOG_ERROR(Service_NFP, "Application area already exist"); + return ApplicationAreaExist; + } + + return RecreateApplicationArea(access_id, data); +} + +Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span<const u8> data) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (data.size() > sizeof(ApplicationArea)) { + LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); + return WrongApplicationAreaSize; + } + + Common::TinyMT rng{}; + std::memcpy(tag_data.application_area.data(), data.data(), data.size()); + // Fill remaining data with random numbers + rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), + sizeof(ApplicationArea) - data.size()); + + // TODO: Investigate why the title id needs to be moddified + tag_data.title_id = system.GetCurrentProcessProgramID(); + tag_data.title_id = tag_data.title_id | 0x30000000ULL; + tag_data.settings.settings.appdata_initialized.Assign(1); + tag_data.application_area_id = access_id; + tag_data.applicaton_write_counter++; + tag_data.unknown = {}; + + return Flush(); +} + +Result NfpDevice::DeleteApplicationArea() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + Common::TinyMT rng{}; + rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea)); + rng.GenerateRandomBytes(&tag_data.title_id, sizeof(u64)); + rng.GenerateRandomBytes(&tag_data.application_area_id, sizeof(u32)); + tag_data.settings.settings.appdata_initialized.Assign(0); + tag_data.applicaton_write_counter++; + tag_data.unknown = {}; + + return Flush(); +} + +u64 NfpDevice::GetHandle() const { + // Generate a handle based of the npad id + return static_cast<u64>(npad_id); +} + +u32 NfpDevice::GetApplicationAreaSize() const { + return sizeof(ApplicationArea); +} + +DeviceState NfpDevice::GetCurrentState() const { + return device_state; +} + +Core::HID::NpadIdType NfpDevice::GetNpadId() const { + return npad_id; +} + +AmiiboName NfpDevice::GetAmiiboName(const AmiiboSettings& settings) const { + std::array<char16_t, amiibo_name_length> settings_amiibo_name{}; + AmiiboName amiibo_name{}; + + // Convert from big endian to little endian + for (std::size_t i = 0; i < amiibo_name_length; i++) { + settings_amiibo_name[i] = static_cast<u16>(settings.amiibo_name[i]); + } + + // Convert from utf16 to utf8 + const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data()); + memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size()); + + return amiibo_name; +} + +void NfpDevice::SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name) { + std::array<char16_t, amiibo_name_length> settings_amiibo_name{}; + + // Convert from utf8 to utf16 + const auto amiibo_name_utf16 = Common::UTF8ToUTF16(amiibo_name.data()); + memcpy(settings_amiibo_name.data(), amiibo_name_utf16.data(), + amiibo_name_utf16.size() * sizeof(char16_t)); + + // Convert from little endian to big endian + for (std::size_t i = 0; i < amiibo_name_length; i++) { + settings.amiibo_name[i] = static_cast<u16_be>(settings_amiibo_name[i]); + } +} + +AmiiboDate NfpDevice::GetAmiiboDate(s64 posix_time) const { + const auto& time_zone_manager = + system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); + Time::TimeZone::CalendarInfo calendar_info{}; + AmiiboDate amiibo_date{}; + + amiibo_date.SetYear(2000); + amiibo_date.SetMonth(1); + amiibo_date.SetDay(1); + + if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) { + amiibo_date.SetYear(calendar_info.time.year); + amiibo_date.SetMonth(calendar_info.time.month); + amiibo_date.SetDay(calendar_info.time.day); + } + + return amiibo_date; +} + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h new file mode 100644 index 000000000..a5b72cf19 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_device.h @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <vector> + +#include "common/common_funcs.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/mii/types.h" +#include "core/hle/service/nfp/nfp_types.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Core { +class System; +} // namespace Core + +namespace Core::HID { +class EmulatedController; +enum class ControllerTriggerType; +enum class NpadIdType : u32; +} // namespace Core::HID + +namespace Service::NFP { +class NfpDevice { +public: + NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, + KernelHelpers::ServiceContext& service_context_, + Kernel::KEvent* availability_change_event_); + ~NfpDevice(); + + void Initialize(); + void Finalize(); + + Result StartDetection(s32 protocol_); + Result StopDetection(); + Result Mount(MountTarget mount_target); + Result Unmount(); + Result Flush(); + + Result GetTagInfo(TagInfo& tag_info) const; + Result GetCommonInfo(CommonInfo& common_info) const; + Result GetModelInfo(ModelInfo& model_info) const; + Result GetRegisterInfo(RegisterInfo& register_info) const; + + Result SetNicknameAndOwner(const AmiiboName& amiibo_name); + Result RestoreAmiibo(); + Result DeleteAllData(); + + Result OpenApplicationArea(u32 access_id); + Result GetApplicationArea(std::vector<u8>& data) const; + Result SetApplicationArea(std::span<const u8> data); + Result CreateApplicationArea(u32 access_id, std::span<const u8> data); + Result RecreateApplicationArea(u32 access_id, std::span<const u8> data); + Result DeleteApplicationArea(); + + u64 GetHandle() const; + u32 GetApplicationAreaSize() const; + DeviceState GetCurrentState() const; + Core::HID::NpadIdType GetNpadId() const; + + Kernel::KReadableEvent& GetActivateEvent() const; + Kernel::KReadableEvent& GetDeactivateEvent() const; + +private: + void NpadUpdate(Core::HID::ControllerTriggerType type); + bool LoadAmiibo(std::span<const u8> data); + void CloseAmiibo(); + + AmiiboName GetAmiiboName(const AmiiboSettings& settings) const; + void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name); + AmiiboDate GetAmiiboDate(s64 posix_time) const; + + bool is_controller_set{}; + int callback_key; + const Core::HID::NpadIdType npad_id; + Core::System& system; + Core::HID::EmulatedController* npad_device = nullptr; + KernelHelpers::ServiceContext& service_context; + Kernel::KEvent* activate_event = nullptr; + Kernel::KEvent* deactivate_event = nullptr; + Kernel::KEvent* availability_change_event = nullptr; + + bool is_data_moddified{}; + bool is_app_area_open{}; + s32 protocol{}; + s64 current_posix_time{}; + MountTarget mount_target{MountTarget::None}; + DeviceState device_state{DeviceState::Unavailable}; + + NTAG215File tag_data{}; + EncryptedNTAG215File encrypted_tag_data{}; +}; + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h new file mode 100644 index 000000000..d8e4cf094 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_result.h @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::NFP { + +constexpr Result DeviceNotFound(ErrorModule::NFP, 64); +constexpr Result InvalidArgument(ErrorModule::NFP, 65); +constexpr Result WrongApplicationAreaSize(ErrorModule::NFP, 68); +constexpr Result WrongDeviceState(ErrorModule::NFP, 73); +constexpr Result NfcDisabled(ErrorModule::NFP, 80); +constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); +constexpr Result TagRemoved(ErrorModule::NFP, 97); +constexpr Result RegistrationIsNotInitialized(ErrorModule::NFP, 120); +constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); +constexpr Result CorruptedData(ErrorModule::NFP, 144); +constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); +constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); +constexpr Result NotAnAmiibo(ErrorModule::NFP, 178); + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h new file mode 100644 index 000000000..867ea2f36 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_types.h @@ -0,0 +1,320 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <array> + +#include "common/swap.h" +#include "core/hle/service/mii/types.h" + +namespace Service::NFP { +static constexpr std::size_t amiibo_name_length = 0xA; + +enum class ServiceType : u32 { + User, + Debug, + System, +}; + +enum class State : u32 { + NonInitialized, + Initialized, +}; + +enum class DeviceState : u32 { + Initialized, + SearchingForTag, + TagFound, + TagRemoved, + TagMounted, + Unavailable, + Finalized, +}; + +enum class ModelType : u32 { + Amiibo, +}; + +enum class MountTarget : u32 { + None, + Rom, + Ram, + All, +}; + +enum class AmiiboType : u8 { + Figure, + Card, + Yarn, +}; + +enum class AmiiboSeries : u8 { + SuperSmashBros, + SuperMario, + ChibiRobo, + YoshiWoollyWorld, + Splatoon, + AnimalCrossing, + EightBitMario, + Skylanders, + Unknown8, + TheLegendOfZelda, + ShovelKnight, + Unknown11, + Kiby, + Pokemon, + MarioSportsSuperstars, + MonsterHunter, + BoxBoy, + Pikmin, + FireEmblem, + Metroid, + Others, + MegaMan, + Diablo, +}; + +enum class TagType : u32 { + None, + Type1, // ISO14443A RW 96-2k bytes 106kbit/s + Type2, // ISO14443A RW/RO 540 bytes 106kbit/s + Type3, // Sony Felica RW/RO 2k bytes 212kbit/s + Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s + Type5, // ISO15693 RW/RO 540 bytes 106kbit/s +}; + +enum class PackedTagType : u8 { + None, + Type1, // ISO14443A RW 96-2k bytes 106kbit/s + Type2, // ISO14443A RW/RO 540 bytes 106kbit/s + Type3, // Sony Felica RW/RO 2k bytes 212kbit/s + Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s + Type5, // ISO15693 RW/RO 540 bytes 106kbit/s +}; + +enum class TagProtocol : u32 { + None, + TypeA, // ISO14443A + TypeB, // ISO14443B + TypeF, // Sony Felica +}; + +using UniqueSerialNumber = std::array<u8, 7>; +using LockBytes = std::array<u8, 2>; +using HashData = std::array<u8, 0x20>; +using ApplicationArea = std::array<u8, 0xD8>; +using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>; + +struct TagUuid { + UniqueSerialNumber uid; + u8 nintendo_id; + LockBytes lock_bytes; +}; +static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size"); + +struct WriteDate { + u16 year; + u8 month; + u8 day; +}; +static_assert(sizeof(WriteDate) == 0x4, "WriteDate is an invalid size"); + +struct AmiiboDate { + u16 raw_date{}; + + u16 GetValue() const { + return Common::swap16(raw_date); + } + + u16 GetYear() const { + return static_cast<u16>(((GetValue() & 0xFE00) >> 9) + 2000); + } + u8 GetMonth() const { + return static_cast<u8>((GetValue() & 0x01E0) >> 5); + } + u8 GetDay() const { + return static_cast<u8>(GetValue() & 0x001F); + } + + WriteDate GetWriteDate() const { + if (!IsValidDate()) { + return { + .year = 2000, + .month = 1, + .day = 1, + }; + } + return { + .year = GetYear(), + .month = GetMonth(), + .day = GetDay(), + }; + } + + void SetYear(u16 year) { + const u16 year_converted = static_cast<u16>((year - 2000) << 9); + raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted); + } + void SetMonth(u8 month) { + const u16 month_converted = static_cast<u16>(month << 5); + raw_date = Common::swap16((GetValue() & ~0x01E0) | month_converted); + } + void SetDay(u8 day) { + const u16 day_converted = static_cast<u16>(day); + raw_date = Common::swap16((GetValue() & ~0x001F) | day_converted); + } + + bool IsValidDate() const { + const bool is_day_valid = GetDay() > 0 && GetDay() < 32; + const bool is_month_valid = GetMonth() >= 0 && GetMonth() < 13; + const bool is_year_valid = GetYear() >= 2000; + return is_year_valid && is_month_valid && is_day_valid; + } +}; +static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); + +struct Settings { + union { + u8 raw{}; + + BitField<4, 1, u8> amiibo_initialized; + BitField<5, 1, u8> appdata_initialized; + }; +}; +static_assert(sizeof(Settings) == 1, "AmiiboDate is an invalid size"); + +struct AmiiboSettings { + Settings settings; + u8 country_code_id; + u16_be crc_counter; // Incremented each time crc is changed + AmiiboDate init_date; + AmiiboDate write_date; + u32_be crc; + std::array<u16_be, amiibo_name_length> amiibo_name; // UTF-16 text +}; +static_assert(sizeof(AmiiboSettings) == 0x20, "AmiiboSettings is an invalid size"); + +struct AmiiboModelInfo { + u16 character_id; + u8 character_variant; + AmiiboType amiibo_type; + u16_be model_number; + AmiiboSeries series; + PackedTagType tag_type; + INSERT_PADDING_BYTES(0x4); // Unknown +}; +static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size"); + +struct NTAG215Password { + u32 PWD; // Password to allow write access + u16 PACK; // Password acknowledge reply + u16 RFUI; // Reserved for future use +}; +static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size"); + +#pragma pack(1) +struct EncryptedAmiiboFile { + u8 constant_value; // Must be A5 + u16_be write_counter; // Number of times the amiibo has been written? + INSERT_PADDING_BYTES(0x1); // Unknown 1 + AmiiboSettings settings; // Encrypted amiibo settings + HashData hmac_tag; // Hash + AmiiboModelInfo model_info; // Encrypted amiibo model info + HashData keygen_salt; // Salt + HashData hmac_data; // Hash + Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data + u64_be title_id; // Encrypted Game id + u16_be applicaton_write_counter; // Encrypted Counter + u32_be application_area_id; // Encrypted Game id + std::array<u8, 0x2> unknown; + std::array<u32, 0x8> unknown2; + ApplicationArea application_area; // Encrypted Game data +}; +static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); + +struct NTAG215File { + LockBytes lock_bytes; // Tag UUID + u16 static_lock; // Set defined pages as read only + u32 compability_container; // Defines available memory + HashData hmac_data; // Hash + u8 constant_value; // Must be A5 + u16_be write_counter; // Number of times the amiibo has been written? + INSERT_PADDING_BYTES(0x1); // Unknown 1 + AmiiboSettings settings; + Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data + u64_be title_id; + u16_be applicaton_write_counter; // Encrypted Counter + u32_be application_area_id; + std::array<u8, 0x2> unknown; + std::array<u32, 0x8> unknown2; + ApplicationArea application_area; // Encrypted Game data + HashData hmac_tag; // Hash + UniqueSerialNumber uid; // Unique serial number + u8 nintendo_id; // Tag UUID + AmiiboModelInfo model_info; + HashData keygen_salt; // Salt + u32 dynamic_lock; // Dynamic lock + u32 CFG0; // Defines memory protected by password + u32 CFG1; // Defines number of verification attempts + NTAG215Password password; // Password data +}; +static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size"); +static_assert(std::is_trivially_copyable_v<NTAG215File>, "NTAG215File must be trivially copyable."); +#pragma pack() + +struct EncryptedNTAG215File { + TagUuid uuid; // Unique serial number + u16 static_lock; // Set defined pages as read only + u32 compability_container; // Defines available memory + EncryptedAmiiboFile user_memory; // Writable data + u32 dynamic_lock; // Dynamic lock + u32 CFG0; // Defines memory protected by password + u32 CFG1; // Defines number of verification attempts + NTAG215Password password; // Password data +}; +static_assert(sizeof(EncryptedNTAG215File) == 0x21C, "EncryptedNTAG215File is an invalid size"); +static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>, + "EncryptedNTAG215File must be trivially copyable."); + +struct TagInfo { + UniqueSerialNumber uuid; + INSERT_PADDING_BYTES(0x3); + u8 uuid_length; + INSERT_PADDING_BYTES(0x15); + TagProtocol protocol; + TagType tag_type; + INSERT_PADDING_BYTES(0x30); +}; +static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); + +struct CommonInfo { + WriteDate last_write_date; + u16 write_counter; + u8 version; + INSERT_PADDING_BYTES(0x1); + u32 application_area_size; + INSERT_PADDING_BYTES(0x34); +}; +static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); + +struct ModelInfo { + u16 character_id; + u8 character_variant; + AmiiboType amiibo_type; + u16 model_number; + AmiiboSeries series; + INSERT_PADDING_BYTES(0x39); // Unknown +}; +static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); + +struct RegisterInfo { + Service::Mii::CharInfo mii_char_info; + WriteDate creation_date; + AmiiboName amiibo_name; + u8 font_region; + INSERT_PADDING_BYTES(0x7A); +}; +static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp index 10b0ef944..4ed53b534 100644 --- a/src/core/hle/service/nfp/nfp_user.cpp +++ b/src/core/hle/service/nfp/nfp_user.cpp @@ -1,19 +1,674 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#include <array> +#include <atomic> + +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hid/hid_types.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/mii/mii_manager.h" +#include "core/hle/service/nfp/nfp_device.h" +#include "core/hle/service/nfp/nfp_result.h" #include "core/hle/service/nfp/nfp_user.h" namespace Service::NFP { -NFP_User::NFP_User(std::shared_ptr<Module> module_, Core::System& system_) - : Interface(std::move(module_), system_, "nfp:user") { +IUser::IUser(Core::System& system_) + : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name} { static const FunctionInfo functions[] = { - {0, &NFP_User::CreateUserInterface, "CreateUserInterface"}, + {0, &IUser::Initialize, "Initialize"}, + {1, &IUser::Finalize, "Finalize"}, + {2, &IUser::ListDevices, "ListDevices"}, + {3, &IUser::StartDetection, "StartDetection"}, + {4, &IUser::StopDetection, "StopDetection"}, + {5, &IUser::Mount, "Mount"}, + {6, &IUser::Unmount, "Unmount"}, + {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, + {8, &IUser::GetApplicationArea, "GetApplicationArea"}, + {9, &IUser::SetApplicationArea, "SetApplicationArea"}, + {10, &IUser::Flush, "Flush"}, + {11, &IUser::Restore, "Restore"}, + {12, &IUser::CreateApplicationArea, "CreateApplicationArea"}, + {13, &IUser::GetTagInfo, "GetTagInfo"}, + {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, + {15, &IUser::GetCommonInfo, "GetCommonInfo"}, + {16, &IUser::GetModelInfo, "GetModelInfo"}, + {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, + {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, + {19, &IUser::GetState, "GetState"}, + {20, &IUser::GetDeviceState, "GetDeviceState"}, + {21, &IUser::GetNpadId, "GetNpadId"}, + {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, + {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, + {24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"}, }; RegisterHandlers(functions); + + availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent"); + + for (u32 device_index = 0; device_index < 10; device_index++) { + devices[device_index] = + std::make_shared<NfpDevice>(Core::HID::IndexToNpadIdType(device_index), system, + service_context, availability_change_event); + } +} + +void IUser::Initialize(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + state = State::Initialized; + + for (auto& device : devices) { + device->Initialize(); + } + + IPC::ResponseBuilder rb{ctx, 2, 0}; + rb.Push(ResultSuccess); +} + +void IUser::Finalize(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFP, "called"); + + state = State::NonInitialized; + + for (auto& device : devices) { + device->Finalize(); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IUser::ListDevices(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFP, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + if (!ctx.CanWriteBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + if (ctx.GetWriteBufferSize() == 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + std::vector<u64> nfp_devices; + const std::size_t max_allowed_devices = ctx.GetWriteBufferSize() / sizeof(u64); + + for (auto& device : devices) { + if (nfp_devices.size() >= max_allowed_devices) { + continue; + } + if (device->GetCurrentState() != DeviceState::Unavailable) { + nfp_devices.push_back(device->GetHandle()); + } + } + + if (nfp_devices.size() == 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + ctx.WriteBuffer(nfp_devices); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast<s32>(nfp_devices.size())); +} + +void IUser::StartDetection(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + const auto nfp_protocol{rp.Pop<s32>()}; + LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->StartDetection(nfp_protocol); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::StopDetection(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->StopDetection(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::Mount(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + const auto model_type{rp.PopEnum<ModelType>()}; + const auto mount_target{rp.PopEnum<MountTarget>()}; + LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle, + model_type, mount_target); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->Mount(mount_target); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::Unmount(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->Unmount(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + const auto access_id{rp.Pop<u32>()}; + LOG_INFO(Service_NFP, "called, device_handle={}, access_id={}", device_handle, access_id); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->OpenApplicationArea(access_id); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + const auto data_size = ctx.GetWriteBufferSize(); + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + if (!ctx.CanWriteBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + std::vector<u8> data(data_size); + const auto result = device.value()->GetApplicationArea(data); + ctx.WriteBuffer(data); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(static_cast<u32>(data_size)); +} + +void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + const auto data{ctx.ReadBuffer()}; + LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}", device_handle, data.size()); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + if (!ctx.CanReadBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->SetApplicationArea(data); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::Flush(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->Flush(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::Restore(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->RestoreAmiibo(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + const auto access_id{rp.Pop<u32>()}; + const auto data{ctx.ReadBuffer()}; + LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle, + access_id, data.size()); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + if (!ctx.CanReadBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->CreateApplicationArea(access_id, data); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + TagInfo tag_info{}; + const auto result = device.value()->GetTagInfo(tag_info); + ctx.WriteBuffer(tag_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + RegisterInfo register_info{}; + const auto result = device.value()->GetRegisterInfo(register_info); + ctx.WriteBuffer(register_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + CommonInfo common_info{}; + const auto result = device.value()->GetCommonInfo(common_info); + ctx.WriteBuffer(common_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + ModelInfo model_info{}; + const auto result = device.value()->GetModelInfo(model_info); + ctx.WriteBuffer(model_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetActivateEvent()); +} + +void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetDeactivateEvent()); +} + +void IUser::GetState(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + + IPC::ResponseBuilder rb{ctx, 3, 0}; + rb.Push(ResultSuccess); + rb.PushEnum(state); +} + +void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetCurrentState()); +} + +void IUser::GetNpadId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetNpadId()); +} + +void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(device.value()->GetApplicationAreaSize()); } -NFP_User::~NFP_User() = default; +void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFP, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(availability_change_event->GetReadableEvent()); +} + +void IUser::RecreateApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + const auto access_id{rp.Pop<u32>()}; + const auto data{ctx.ReadBuffer()}; + LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle, + access_id, data.size()); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->RecreateApplicationArea(access_id, data); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +std::optional<std::shared_ptr<NfpDevice>> IUser::GetNfpDevice(u64 handle) { + for (auto& device : devices) { + if (device->GetHandle() == handle) { + return device; + } + } + return std::nullopt; +} } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h index 7f3c124f6..68c60ae82 100644 --- a/src/core/hle/service/nfp/nfp_user.h +++ b/src/core/hle/service/nfp/nfp_user.h @@ -1,17 +1,54 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nfp/nfp.h" +#include "core/hle/service/nfp/nfp_types.h" namespace Service::NFP { +class NfpDevice; -class NFP_User final : public Module::Interface { +class IUser final : public ServiceFramework<IUser> { public: - explicit NFP_User(std::shared_ptr<Module> module_, Core::System& system_); - ~NFP_User() override; + explicit IUser(Core::System& system_); + +private: + void Initialize(Kernel::HLERequestContext& ctx); + void Finalize(Kernel::HLERequestContext& ctx); + void ListDevices(Kernel::HLERequestContext& ctx); + void StartDetection(Kernel::HLERequestContext& ctx); + void StopDetection(Kernel::HLERequestContext& ctx); + void Mount(Kernel::HLERequestContext& ctx); + void Unmount(Kernel::HLERequestContext& ctx); + void OpenApplicationArea(Kernel::HLERequestContext& ctx); + void GetApplicationArea(Kernel::HLERequestContext& ctx); + void SetApplicationArea(Kernel::HLERequestContext& ctx); + void Flush(Kernel::HLERequestContext& ctx); + void Restore(Kernel::HLERequestContext& ctx); + void CreateApplicationArea(Kernel::HLERequestContext& ctx); + void GetTagInfo(Kernel::HLERequestContext& ctx); + void GetRegisterInfo(Kernel::HLERequestContext& ctx); + void GetCommonInfo(Kernel::HLERequestContext& ctx); + void GetModelInfo(Kernel::HLERequestContext& ctx); + void AttachActivateEvent(Kernel::HLERequestContext& ctx); + void AttachDeactivateEvent(Kernel::HLERequestContext& ctx); + void GetState(Kernel::HLERequestContext& ctx); + void GetDeviceState(Kernel::HLERequestContext& ctx); + void GetNpadId(Kernel::HLERequestContext& ctx); + void GetApplicationAreaSize(Kernel::HLERequestContext& ctx); + void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx); + void RecreateApplicationArea(Kernel::HLERequestContext& ctx); + + std::optional<std::shared_ptr<NfpDevice>> GetNfpDevice(u64 handle); + + KernelHelpers::ServiceContext service_context; + + std::array<std::shared_ptr<NfpDevice>, 10> devices{}; + + State state{State::NonInitialized}; + Kernel::KEvent* availability_change_event; }; } // namespace Service::NFP diff --git a/src/core/hle/service/ngct/ngct.cpp b/src/core/hle/service/ngct/ngct.cpp index 8ec7d5266..8af8a835d 100644 --- a/src/core/hle/service/ngct/ngct.cpp +++ b/src/core/hle/service/ngct/ngct.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/string_util.h" #include "core/core.h" diff --git a/src/core/hle/service/ngct/ngct.h b/src/core/hle/service/ngct/ngct.h index 1f2a47b78..370bd4a25 100644 --- a/src/core/hle/service/ngct/ngct.h +++ b/src/core/hle/service/ngct/ngct.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index a253dd066..e3ef06481 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -1,14 +1,11 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/kernel.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nifm/nifm.h" -#include "core/hle/service/service.h" namespace { @@ -20,8 +17,8 @@ namespace { } // Anonymous namespace -#include "core/network/network.h" -#include "core/network/network_interface.h" +#include "core/internal_network/network.h" +#include "core/internal_network/network_interface.h" namespace Service::NIFM { @@ -32,6 +29,19 @@ enum class RequestState : u32 { Connected = 3, }; +enum class InternetConnectionType : u8 { + WiFi = 1, + Ethernet = 2, +}; + +enum class InternetConnectionStatus : u8 { + ConnectingUnknown1, + ConnectingUnknown2, + ConnectingUnknown3, + ConnectingUnknown4, + Connected, +}; + struct IpAddressSetting { bool is_automatic{}; Network::IPv4Address current_address{}; @@ -260,135 +270,45 @@ public: } }; -class IGeneralService final : public ServiceFramework<IGeneralService> { -public: - explicit IGeneralService(Core::System& system_); - -private: - void GetClientId(Kernel::HLERequestContext& ctx) { - static constexpr u32 client_id = 1; - LOG_WARNING(Service_NIFM, "(STUBBED) called"); +void IGeneralService::GetClientId(Kernel::HLERequestContext& ctx) { + static constexpr u32 client_id = 1; + LOG_WARNING(Service_NIFM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid - } - void CreateScanRequest(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NIFM, "called"); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid +} - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; +void IGeneralService::CreateScanRequest(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIFM, "called"); - rb.Push(ResultSuccess); - rb.PushIpcInterface<IScanRequest>(system); - } - void CreateRequest(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NIFM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IScanRequest>(system); +} - rb.Push(ResultSuccess); - rb.PushIpcInterface<IRequest>(system); - } - void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); +void IGeneralService::CreateRequest(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIFM, "called"); - const auto net_iface = Network::GetSelectedNetworkInterface(); - - const SfNetworkProfileData network_profile_data = [&net_iface] { - if (!net_iface) { - return SfNetworkProfileData{}; - } - - return SfNetworkProfileData{ - .ip_setting_data{ - .ip_address_setting{ - .is_automatic{true}, - .current_address{Network::TranslateIPv4(net_iface->ip_address)}, - .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, - .gateway{Network::TranslateIPv4(net_iface->gateway)}, - }, - .dns_setting{ - .is_automatic{true}, - .primary_dns{1, 1, 1, 1}, - .secondary_dns{1, 0, 0, 1}, - }, - .proxy_setting{ - .enabled{false}, - .port{}, - .proxy_server{}, - .automatic_auth_enabled{}, - .user{}, - .password{}, - }, - .mtu{1500}, - }, - .uuid{0xdeadbeef, 0xdeadbeef}, - .network_name{"yuzu Network"}, - .wireless_setting_data{ - .ssid_length{12}, - .ssid{"yuzu Network"}, - .passphrase{"yuzupassword"}, - }, - }; - }(); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - ctx.WriteBuffer(network_profile_data); + rb.Push(ResultSuccess); + rb.PushIpcInterface<IRequest>(system); +} - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); +void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); + const auto net_iface = Network::GetSelectedNetworkInterface(); - auto ipv4 = Network::GetHostIPv4Address(); - if (!ipv4) { - LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0"); - ipv4.emplace(Network::IPv4Address{0, 0, 0, 0}); + SfNetworkProfileData network_profile_data = [&net_iface] { + if (!net_iface) { + return SfNetworkProfileData{}; } - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw(*ipv4); - } - void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NIFM, "called"); - - ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, - "SfNetworkProfileData is not the correct size"); - u128 uuid{}; - auto buffer = ctx.ReadBuffer(); - std::memcpy(&uuid, buffer.data() + 8, sizeof(u128)); - - IPC::ResponseBuilder rb{ctx, 6, 0, 1}; - - rb.Push(ResultSuccess); - rb.PushIpcInterface<INetworkProfile>(system); - rb.PushRaw<u128>(uuid); - } - void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); - - struct IpConfigInfo { - IpAddressSetting ip_address_setting{}; - DnsSetting dns_setting{}; - }; - static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), - "IpConfigInfo has incorrect size."); - - const auto net_iface = Network::GetSelectedNetworkInterface(); - - const IpConfigInfo ip_config_info = [&net_iface] { - if (!net_iface) { - return IpConfigInfo{}; - } - - return IpConfigInfo{ + return SfNetworkProfileData{ + .ip_setting_data{ .ip_address_setting{ .is_automatic{true}, .current_address{Network::TranslateIPv4(net_iface->ip_address)}, @@ -400,46 +320,178 @@ private: .primary_dns{1, 1, 1, 1}, .secondary_dns{1, 0, 0, 1}, }, - }; - }(); + .proxy_setting{ + .enabled{false}, + .port{}, + .proxy_server{}, + .automatic_auth_enabled{}, + .user{}, + .password{}, + }, + .mtu{1500}, + }, + .uuid{0xdeadbeef, 0xdeadbeef}, + .network_name{"yuzu Network"}, + .wireless_setting_data{ + .ssid_length{12}, + .ssid{"yuzu Network"}, + .passphrase{"yuzupassword"}, + }, + }; + }(); - IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; - rb.Push(ResultSuccess); - rb.PushRaw<IpConfigInfo>(ip_config_info); + // When we're connected to a room, spoof the hosts IP address + if (auto room_member = network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + network_profile_data.ip_setting_data.ip_address_setting.current_address = + room_member->GetFakeIpAddress(); + } } - void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u8>(0); + ctx.WriteBuffer(network_profile_data); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IGeneralService::RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IGeneralService::GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + auto ipv4 = Network::GetHostIPv4Address(); + if (!ipv4) { + LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0"); + ipv4.emplace(Network::IPv4Address{0, 0, 0, 0}); } - void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - if (Network::GetHostIPv4Address().has_value()) { - rb.Push<u8>(1); - } else { - rb.Push<u8>(0); + // When we're connected to a room, spoof the hosts IP address + if (auto room_member = network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + ipv4 = room_member->GetFakeIpAddress(); } } - void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - if (Network::GetHostIPv4Address().has_value()) { - rb.Push<u8>(1); - } else { - rb.Push<u8>(0); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(*ipv4); +} + +void IGeneralService::CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIFM, "called"); + + ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "SfNetworkProfileData is not the correct size"); + u128 uuid{}; + auto buffer = ctx.ReadBuffer(); + std::memcpy(&uuid, buffer.data() + 8, sizeof(u128)); + + IPC::ResponseBuilder rb{ctx, 6, 0, 1}; + + rb.Push(ResultSuccess); + rb.PushIpcInterface<INetworkProfile>(system); + rb.PushRaw<u128>(uuid); +} + +void IGeneralService::GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + struct IpConfigInfo { + IpAddressSetting ip_address_setting{}; + DnsSetting dns_setting{}; + }; + static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), + "IpConfigInfo has incorrect size."); + + const auto net_iface = Network::GetSelectedNetworkInterface(); + + IpConfigInfo ip_config_info = [&net_iface] { + if (!net_iface) { + return IpConfigInfo{}; + } + + return IpConfigInfo{ + .ip_address_setting{ + .is_automatic{true}, + .current_address{Network::TranslateIPv4(net_iface->ip_address)}, + .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, + .gateway{Network::TranslateIPv4(net_iface->gateway)}, + }, + .dns_setting{ + .is_automatic{true}, + .primary_dns{1, 1, 1, 1}, + .secondary_dns{1, 0, 0, 1}, + }, + }; + }(); + + // When we're connected to a room, spoof the hosts IP address + if (auto room_member = network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + ip_config_info.ip_address_setting.current_address = room_member->GetFakeIpAddress(); } } -}; + + IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; + rb.Push(ResultSuccess); + rb.PushRaw<IpConfigInfo>(ip_config_info); +} + +void IGeneralService::IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u8>(1); +} + +void IGeneralService::GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + struct Output { + InternetConnectionType type{InternetConnectionType::WiFi}; + u8 wifi_strength{3}; + InternetConnectionStatus state{InternetConnectionStatus::Connected}; + }; + static_assert(sizeof(Output) == 0x3, "Output has incorrect size."); + + constexpr Output out{}; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(out); +} + +void IGeneralService::IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + if (Network::GetHostIPv4Address().has_value()) { + rb.Push<u8>(1); + } else { + rb.Push<u8>(0); + } +} + +void IGeneralService::IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { + LOG_ERROR(Service_NIFM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + if (Network::GetHostIPv4Address().has_value()) { + rb.Push<u8>(1); + } else { + rb.Push<u8>(0); + } +} IGeneralService::IGeneralService(Core::System& system_) - : ServiceFramework{system_, "IGeneralService"} { + : ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} { // clang-format off static const FunctionInfo functions[] = { {1, &IGeneralService::GetClientId, "GetClientId"}, @@ -458,7 +510,7 @@ IGeneralService::IGeneralService(Core::System& system_) {15, &IGeneralService::GetCurrentIpConfigInfo, "GetCurrentIpConfigInfo"}, {16, nullptr, "SetWirelessCommunicationEnabled"}, {17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"}, - {18, nullptr, "GetInternetConnectionStatus"}, + {18, &IGeneralService::GetInternetConnectionStatus, "GetInternetConnectionStatus"}, {19, nullptr, "SetEthernetCommunicationEnabled"}, {20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"}, {21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"}, @@ -490,6 +542,8 @@ IGeneralService::IGeneralService(Core::System& system_) RegisterHandlers(functions); } +IGeneralService::~IGeneralService() = default; + class NetworkInterface final : public ServiceFramework<NetworkInterface> { public: explicit NetworkInterface(const char* name, Core::System& system_) diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h index c3dd4f386..48161be28 100644 --- a/src/core/hle/service/nifm/nifm.h +++ b/src/core/hle/service/nifm/nifm.h @@ -1,9 +1,13 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "core/hle/service/service.h" +#include "network/network.h" +#include "network/room.h" +#include "network/room_member.h" + namespace Core { class System; } @@ -17,4 +21,26 @@ namespace Service::NIFM { /// Registers all NIFM services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); +class IGeneralService final : public ServiceFramework<IGeneralService> { +public: + explicit IGeneralService(Core::System& system_); + ~IGeneralService() override; + +private: + void GetClientId(Kernel::HLERequestContext& ctx); + void CreateScanRequest(Kernel::HLERequestContext& ctx); + void CreateRequest(Kernel::HLERequestContext& ctx); + void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx); + void RemoveNetworkProfile(Kernel::HLERequestContext& ctx); + void GetCurrentIpAddress(Kernel::HLERequestContext& ctx); + void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx); + void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx); + void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx); + void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx); + void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx); + void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx); + + Network::RoomNetwork& network; +}; + } // namespace Service::NIFM diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index 4fc23a958..b2bb7426d 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <chrono> #include <ctime> diff --git a/src/core/hle/service/nim/nim.h b/src/core/hle/service/nim/nim.h index 571153fe6..8f6ff28e8 100644 --- a/src/core/hle/service/nim/nim.h +++ b/src/core/hle/service/nim/nim.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp index 32533cd94..8133711c2 100644 --- a/src/core/hle/service/npns/npns.cpp +++ b/src/core/hle/service/npns/npns.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/npns/npns.h b/src/core/hle/service/npns/npns.h index 3b7596b6b..84e6ec437 100644 --- a/src/core/hle/service/npns/npns.h +++ b/src/core/hle/service/npns/npns.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/ns/errors.h b/src/core/hle/service/ns/errors.h index f4aea8a65..8a7621798 100644 --- a/src/core/hle/service/ns/errors.h +++ b/src/core/hle/service/ns/errors.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,5 +7,5 @@ namespace Service::NS { -constexpr ResultCode ERR_APPLICATION_LANGUAGE_NOT_FOUND{ErrorModule::NS, 300}; +constexpr Result ERR_APPLICATION_LANGUAGE_NOT_FOUND{ErrorModule::NS, 300}; }
\ No newline at end of file diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/iplatform_service_manager.cpp index 74cc45f1e..fd047ff26 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/iplatform_service_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstring> @@ -21,7 +20,7 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/physical_memory.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/ns/pl_u.h" +#include "core/hle/service/ns/iplatform_service_manager.h" namespace Service::NS { @@ -100,7 +99,7 @@ static u32 GetU32Swapped(const u8* data) { return Common::swap32(value); } -struct PL_U::Impl { +struct IPlatformServiceManager::Impl { const FontRegion& GetSharedFontRegion(std::size_t index) const { if (index >= shared_font_regions.size() || shared_font_regions.empty()) { // No font fallback @@ -135,16 +134,16 @@ struct PL_U::Impl { std::vector<FontRegion> shared_font_regions; }; -PL_U::PL_U(Core::System& system_) - : ServiceFramework{system_, "pl:u"}, impl{std::make_unique<Impl>()} { +IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const char* service_name_) + : ServiceFramework{system_, service_name_}, impl{std::make_unique<Impl>()} { // clang-format off 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"}, - {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, + {0, &IPlatformServiceManager::RequestLoad, "RequestLoad"}, + {1, &IPlatformServiceManager::GetLoadState, "GetLoadState"}, + {2, &IPlatformServiceManager::GetSize, "GetSize"}, + {3, &IPlatformServiceManager::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"}, + {4, &IPlatformServiceManager::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, + {5, &IPlatformServiceManager::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, {6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"}, {100, nullptr, "RequestApplicationFunctionAuthorization"}, {101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"}, @@ -207,9 +206,9 @@ PL_U::PL_U(Core::System& system_) } } -PL_U::~PL_U() = default; +IPlatformServiceManager::~IPlatformServiceManager() = default; -void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { +void IPlatformServiceManager::RequestLoad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 shared_font_type{rp.Pop<u32>()}; // Games don't call this so all fonts should be loaded @@ -219,7 +218,7 @@ void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } -void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) { +void IPlatformServiceManager::GetLoadState(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 font_id{rp.Pop<u32>()}; LOG_DEBUG(Service_NS, "called, font_id={}", font_id); @@ -229,7 +228,7 @@ void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) { rb.Push<u32>(static_cast<u32>(LoadState::Done)); } -void PL_U::GetSize(Kernel::HLERequestContext& ctx) { +void IPlatformServiceManager::GetSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 font_id{rp.Pop<u32>()}; LOG_DEBUG(Service_NS, "called, font_id={}", font_id); @@ -239,7 +238,7 @@ void PL_U::GetSize(Kernel::HLERequestContext& ctx) { rb.Push<u32>(impl->GetSharedFontRegion(font_id).size); } -void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { +void IPlatformServiceManager::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 font_id{rp.Pop<u32>()}; LOG_DEBUG(Service_NS, "called, font_id={}", font_id); @@ -249,7 +248,7 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { rb.Push<u32>(impl->GetSharedFontRegion(font_id).offset); } -void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { +void IPlatformServiceManager::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { // Map backing memory for the font data LOG_DEBUG(Service_NS, "called"); @@ -262,7 +261,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { rb.PushCopyObjects(&kernel.GetFontSharedMem()); } -void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { +void IPlatformServiceManager::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for LOG_DEBUG(Service_NS, "called, language_code={:X}", language_code); diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/iplatform_service_manager.h index f920c7f69..ed6eda89f 100644 --- a/src/core/hle/service/ns/pl_u.h +++ b/src/core/hle/service/ns/iplatform_service_manager.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -37,10 +36,10 @@ constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{ void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output); void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, std::size_t& offset); -class PL_U final : public ServiceFramework<PL_U> { +class IPlatformServiceManager final : public ServiceFramework<IPlatformServiceManager> { public: - explicit PL_U(Core::System& system_); - ~PL_U() override; + explicit IPlatformServiceManager(Core::System& system_, const char* service_name_); + ~IPlatformServiceManager() override; private: void RequestLoad(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/ns/language.cpp b/src/core/hle/service/ns/language.cpp index e01c6be47..036a1e9b7 100644 --- a/src/core/hle/service/ns/language.cpp +++ b/src/core/hle/service/ns/language.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/ns/language.h" #include "core/hle/service/set/set.h" diff --git a/src/core/hle/service/ns/language.h b/src/core/hle/service/ns/language.h index 2cc8e4806..ab6b71029 100644 --- a/src/core/hle/service/ns/language.h +++ b/src/core/hle/service/ns/language.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 5eaad0474..f7318c3cb 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "common/settings.h" @@ -10,10 +9,10 @@ #include "core/file_sys/vfs.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/ns/errors.h" +#include "core/hle/service/ns/iplatform_service_manager.h" #include "core/hle/service/ns/language.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/ns/pdm_qry.h" -#include "core/hle/service/ns/pl_u.h" #include "core/hle/service/set/set.h" namespace Service::NS { @@ -765,7 +764,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system std::make_shared<PDM_QRY>(system)->InstallAsService(service_manager); - std::make_shared<PL_U>(system)->InstallAsService(service_manager); + std::make_shared<IPlatformServiceManager>(system, "pl:s")->InstallAsService(service_manager); + std::make_shared<IPlatformServiceManager>(system, "pl:u")->InstallAsService(service_manager); } } // namespace Service::NS diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h index 43540b0fb..4dc191518 100644 --- a/src/core/hle/service/ns/ns.h +++ b/src/core/hle/service/ns/ns.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/ns/pdm_qry.cpp b/src/core/hle/service/ns/pdm_qry.cpp index 36ce46353..aac8f573f 100644 --- a/src/core/hle/service/ns/pdm_qry.cpp +++ b/src/core/hle/service/ns/pdm_qry.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> @@ -9,7 +8,6 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/service/ns/pdm_qry.h" #include "core/hle/service/service.h" -#include "core/hle/service/sm/sm.h" namespace Service::NS { diff --git a/src/core/hle/service/ns/pdm_qry.h b/src/core/hle/service/ns/pdm_qry.h index 516136314..abcc3bef3 100644 --- a/src/core/hle/service/ns/pdm_qry.h +++ b/src/core/hle/service/ns/pdm_qry.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp new file mode 100644 index 000000000..37ca24f5d --- /dev/null +++ b/src/core/hle/service/nvdrv/core/container.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" +#include "video_core/host1x/host1x.h" + +namespace Service::Nvidia::NvCore { + +struct ContainerImpl { + explicit ContainerImpl(Tegra::Host1x::Host1x& host1x_) + : file{host1x_}, manager{host1x_}, device_file_data{} {} + NvMap file; + SyncpointManager manager; + Container::Host1xDeviceFileData device_file_data; +}; + +Container::Container(Tegra::Host1x::Host1x& host1x_) { + impl = std::make_unique<ContainerImpl>(host1x_); +} + +Container::~Container() = default; + +NvMap& Container::GetNvMapFile() { + return impl->file; +} + +const NvMap& Container::GetNvMapFile() const { + return impl->file; +} + +Container::Host1xDeviceFileData& Container::Host1xDeviceFile() { + return impl->device_file_data; +} + +const Container::Host1xDeviceFileData& Container::Host1xDeviceFile() const { + return impl->device_file_data; +} + +SyncpointManager& Container::GetSyncpointManager() { + return impl->manager; +} + +const SyncpointManager& Container::GetSyncpointManager() const { + return impl->manager; +} + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h new file mode 100644 index 000000000..b4b63ac90 --- /dev/null +++ b/src/core/hle/service/nvdrv/core/container.h @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <deque> +#include <memory> +#include <unordered_map> + +#include "core/hle/service/nvdrv/nvdata.h" + +namespace Tegra::Host1x { +class Host1x; +} // namespace Tegra::Host1x + +namespace Service::Nvidia::NvCore { + +class NvMap; +class SyncpointManager; + +struct ContainerImpl; + +class Container { +public: + explicit Container(Tegra::Host1x::Host1x& host1x); + ~Container(); + + NvMap& GetNvMapFile(); + + const NvMap& GetNvMapFile() const; + + SyncpointManager& GetSyncpointManager(); + + const SyncpointManager& GetSyncpointManager() const; + + struct Host1xDeviceFileData { + std::unordered_map<DeviceFD, u32> fd_to_id{}; + std::deque<u32> syncpts_accumulated{}; + u32 nvdec_next_id{}; + u32 vic_next_id{}; + }; + + Host1xDeviceFileData& Host1xDeviceFile(); + + const Host1xDeviceFileData& Host1xDeviceFile() const; + +private: + std::unique_ptr<ContainerImpl> impl; +}; + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp new file mode 100644 index 000000000..fbd8a74a5 --- /dev/null +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -0,0 +1,272 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/memory.h" +#include "video_core/host1x/host1x.h" + +using Core::Memory::YUZU_PAGESIZE; + +namespace Service::Nvidia::NvCore { +NvMap::Handle::Handle(u64 size_, Id id_) + : size(size_), aligned_size(size), orig_size(size), id(id_) { + flags.raw = 0; +} + +NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) { + std::scoped_lock lock(mutex); + + // Handles cannot be allocated twice + if (allocated) { + return NvResult::AccessDenied; + } + + flags = pFlags; + kind = pKind; + align = pAlign < YUZU_PAGESIZE ? YUZU_PAGESIZE : pAlign; + + // This flag is only applicable for handles with an address passed + if (pAddress) { + flags.keep_uncached_after_free.Assign(0); + } else { + LOG_CRITICAL(Service_NVDRV, + "Mapping nvmap handles without a CPU side address is unimplemented!"); + } + + size = Common::AlignUp(size, YUZU_PAGESIZE); + aligned_size = Common::AlignUp(size, align); + address = pAddress; + allocated = true; + + return NvResult::Success; +} + +NvResult NvMap::Handle::Duplicate(bool internal_session) { + std::scoped_lock lock(mutex); + // Unallocated handles cannot be duplicated as duplication requires memory accounting (in HOS) + if (!allocated) [[unlikely]] { + return NvResult::BadValue; + } + + // If we internally use FromId the duplication tracking of handles won't work accurately due to + // us not implementing per-process handle refs. + if (internal_session) { + internal_dupes++; + } else { + dupes++; + } + + return NvResult::Success; +} + +NvMap::NvMap(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {} + +void NvMap::AddHandle(std::shared_ptr<Handle> handle_description) { + std::scoped_lock lock(handles_lock); + + handles.emplace(handle_description->id, std::move(handle_description)); +} + +void NvMap::UnmapHandle(Handle& handle_description) { + // Remove pending unmap queue entry if needed + if (handle_description.unmap_queue_entry) { + unmap_queue.erase(*handle_description.unmap_queue_entry); + handle_description.unmap_queue_entry.reset(); + } + + // Free and unmap the handle from the SMMU + host1x.MemoryManager().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address), + handle_description.aligned_size); + host1x.Allocator().Free(handle_description.pin_virt_address, + static_cast<u32>(handle_description.aligned_size)); + handle_description.pin_virt_address = 0; +} + +bool NvMap::TryRemoveHandle(const Handle& handle_description) { + // No dupes left, we can remove from handle map + if (handle_description.dupes == 0 && handle_description.internal_dupes == 0) { + std::scoped_lock lock(handles_lock); + + auto it{handles.find(handle_description.id)}; + if (it != handles.end()) { + handles.erase(it); + } + + return true; + } else { + return false; + } +} + +NvResult NvMap::CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out) { + if (!size) [[unlikely]] { + return NvResult::BadValue; + } + + u32 id{next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed)}; + auto handle_description{std::make_shared<Handle>(size, id)}; + AddHandle(handle_description); + + result_out = handle_description; + return NvResult::Success; +} + +std::shared_ptr<NvMap::Handle> NvMap::GetHandle(Handle::Id handle) { + std::scoped_lock lock(handles_lock); + try { + return handles.at(handle); + } catch (std::out_of_range&) { + return nullptr; + } +} + +VAddr NvMap::GetHandleAddress(Handle::Id handle) { + std::scoped_lock lock(handles_lock); + try { + return handles.at(handle)->address; + } catch (std::out_of_range&) { + return 0; + } +} + +u32 NvMap::PinHandle(NvMap::Handle::Id handle) { + auto handle_description{GetHandle(handle)}; + if (!handle_description) [[unlikely]] { + return 0; + } + + std::scoped_lock lock(handle_description->mutex); + if (!handle_description->pins) { + // If we're in the unmap queue we can just remove ourselves and return since we're already + // mapped + { + // Lock now to prevent our queue entry from being removed for allocation in-between the + // following check and erase + std::scoped_lock queueLock(unmap_queue_lock); + if (handle_description->unmap_queue_entry) { + unmap_queue.erase(*handle_description->unmap_queue_entry); + handle_description->unmap_queue_entry.reset(); + + handle_description->pins++; + return handle_description->pin_virt_address; + } + } + + // If not then allocate some space and map it + u32 address{}; + auto& smmu_allocator = host1x.Allocator(); + auto& smmu_memory_manager = host1x.MemoryManager(); + while (!(address = + smmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size)))) { + // Free handles until the allocation succeeds + std::scoped_lock queueLock(unmap_queue_lock); + if (auto freeHandleDesc{unmap_queue.front()}) { + // Handles in the unmap queue are guaranteed not to be pinned so don't bother + // checking if they are before unmapping + std::scoped_lock freeLock(freeHandleDesc->mutex); + if (handle_description->pin_virt_address) + UnmapHandle(*freeHandleDesc); + } else { + LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!"); + } + } + + smmu_memory_manager.Map(static_cast<GPUVAddr>(address), handle_description->address, + handle_description->aligned_size); + handle_description->pin_virt_address = address; + } + + handle_description->pins++; + return handle_description->pin_virt_address; +} + +void NvMap::UnpinHandle(Handle::Id handle) { + auto handle_description{GetHandle(handle)}; + if (!handle_description) { + return; + } + + std::scoped_lock lock(handle_description->mutex); + if (--handle_description->pins < 0) { + LOG_WARNING(Service_NVDRV, "Pin count imbalance detected!"); + } else if (!handle_description->pins) { + std::scoped_lock queueLock(unmap_queue_lock); + + // Add to the unmap queue allowing this handle's memory to be freed if needed + unmap_queue.push_back(handle_description); + handle_description->unmap_queue_entry = std::prev(unmap_queue.end()); + } +} + +void NvMap::DuplicateHandle(Handle::Id handle, bool internal_session) { + auto handle_description{GetHandle(handle)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Unregistered handle!"); + return; + } + + auto result = handle_description->Duplicate(internal_session); + if (result != NvResult::Success) { + LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!"); + } +} + +std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool internal_session) { + std::weak_ptr<Handle> hWeak{GetHandle(handle)}; + FreeInfo freeInfo; + + // We use a weak ptr here so we can tell when the handle has been freed and report that back to + // guest + if (auto handle_description = hWeak.lock()) { + std::scoped_lock lock(handle_description->mutex); + + if (internal_session) { + if (--handle_description->internal_dupes < 0) + LOG_WARNING(Service_NVDRV, "Internal duplicate count imbalance detected!"); + } else { + if (--handle_description->dupes < 0) { + LOG_WARNING(Service_NVDRV, "User duplicate count imbalance detected!"); + } else if (handle_description->dupes == 0) { + // Force unmap the handle + if (handle_description->pin_virt_address) { + std::scoped_lock queueLock(unmap_queue_lock); + UnmapHandle(*handle_description); + } + + handle_description->pins = 0; + } + } + + // Try to remove the shared ptr to the handle from the map, if nothing else is using the + // handle then it will now be freed when `handle_description` goes out of scope + if (TryRemoveHandle(*handle_description)) { + LOG_DEBUG(Service_NVDRV, "Removed nvmap handle: {}", handle); + } else { + LOG_DEBUG(Service_NVDRV, + "Tried to free nvmap handle: {} but didn't as it still has duplicates", + handle); + } + + freeInfo = { + .address = handle_description->address, + .size = handle_description->size, + .was_uncached = handle_description->flags.map_uncached.Value() != 0, + }; + } else { + return std::nullopt; + } + + // Handle hasn't been freed from memory, set address to 0 to mark that the handle wasn't freed + if (!hWeak.expired()) { + LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle); + freeInfo.address = 0; + } + + return freeInfo; +} + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h new file mode 100644 index 000000000..b9dd3801f --- /dev/null +++ b/src/core/hle/service/nvdrv/core/nvmap.h @@ -0,0 +1,175 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <atomic> +#include <list> +#include <memory> +#include <mutex> +#include <optional> +#include <unordered_map> +#include <assert.h> + +#include "common/bit_field.h" +#include "common/common_types.h" +#include "core/hle/service/nvdrv/nvdata.h" + +namespace Tegra { + +namespace Host1x { +class Host1x; +} // namespace Host1x + +} // namespace Tegra + +namespace Service::Nvidia::NvCore { +/** + * @brief The nvmap core class holds the global state for nvmap and provides methods to manage + * handles + */ +class NvMap { +public: + /** + * @brief A handle to a contiguous block of memory in an application's address space + */ + struct Handle { + std::mutex mutex; + + u64 align{}; //!< The alignment to use when pinning the handle onto the SMMU + u64 size; //!< Page-aligned size of the memory the handle refers to + u64 aligned_size; //!< `align`-aligned size of the memory the handle refers to + u64 orig_size; //!< Original unaligned size of the memory this handle refers to + + s32 dupes{1}; //!< How many guest references there are to this handle + s32 internal_dupes{0}; //!< How many emulator-internal references there are to this handle + + using Id = u32; + Id id; //!< A globally unique identifier for this handle + + s32 pins{}; + u32 pin_virt_address{}; + std::optional<typename std::list<std::shared_ptr<Handle>>::iterator> unmap_queue_entry{}; + + union Flags { + u32 raw; + BitField<0, 1, u32> map_uncached; //!< If the handle should be mapped as uncached + BitField<2, 1, u32> keep_uncached_after_free; //!< Only applicable when the handle was + //!< allocated with a fixed address + BitField<4, 1, u32> _unk0_; //!< Passed to IOVMM for pins + } flags{}; + static_assert(sizeof(Flags) == sizeof(u32)); + + u64 address{}; //!< The memory location in the guest's AS that this handle corresponds to, + //!< this can also be in the nvdrv tmem + bool is_shared_mem_mapped{}; //!< If this nvmap has been mapped with the MapSharedMem IPC + //!< call + + u8 kind{}; //!< Used for memory compression + bool allocated{}; //!< If the handle has been allocated with `Alloc` + + u64 dma_map_addr{}; //! remove me after implementing pinning. + + Handle(u64 size, Id id); + + /** + * @brief Sets up the handle with the given memory config, can allocate memory from the tmem + * if a 0 address is passed + */ + [[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress); + + /** + * @brief Increases the dupe counter of the handle for the given session + */ + [[nodiscard]] NvResult Duplicate(bool internal_session); + + /** + * @brief Obtains a pointer to the handle's memory and marks the handle it as having been + * mapped + */ + u8* GetPointer() { + if (!address) { + return nullptr; + } + + is_shared_mem_mapped = true; + return reinterpret_cast<u8*>(address); + } + }; + + /** + * @brief Encapsulates the result of a FreeHandle operation + */ + struct FreeInfo { + u64 address; //!< Address the handle referred to before deletion + u64 size; //!< Page-aligned handle size + bool was_uncached; //!< If the handle was allocated as uncached + }; + + explicit NvMap(Tegra::Host1x::Host1x& host1x); + + /** + * @brief Creates an unallocated handle of the given size + */ + [[nodiscard]] NvResult CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out); + + std::shared_ptr<Handle> GetHandle(Handle::Id handle); + + VAddr GetHandleAddress(Handle::Id handle); + + /** + * @brief Maps a handle into the SMMU address space + * @note This operation is refcounted, the number of calls to this must eventually match the + * number of calls to `UnpinHandle` + * @return The SMMU virtual address that the handle has been mapped to + */ + u32 PinHandle(Handle::Id handle); + + /** + * @brief When this has been called an equal number of times to `PinHandle` for the supplied + * handle it will be added to a list of handles to be freed when necessary + */ + void UnpinHandle(Handle::Id handle); + + /** + * @brief Tries to duplicate a handle + */ + void DuplicateHandle(Handle::Id handle, bool internal_session = false); + + /** + * @brief Tries to free a handle and remove a single dupe + * @note If a handle has no dupes left and has no other users a FreeInfo struct will be returned + * describing the prior state of the handle + */ + std::optional<FreeInfo> FreeHandle(Handle::Id handle, bool internal_session); + +private: + std::list<std::shared_ptr<Handle>> unmap_queue{}; + std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue` + + std::unordered_map<Handle::Id, std::shared_ptr<Handle>> + handles{}; //!< Main owning map of handles + std::mutex handles_lock; //!< Protects access to `handles` + + static constexpr u32 HandleIdIncrement{ + 4}; //!< Each new handle ID is an increment of 4 from the previous + std::atomic<u32> next_handle_id{HandleIdIncrement}; + Tegra::Host1x::Host1x& host1x; + + void AddHandle(std::shared_ptr<Handle> handle); + + /** + * @brief Unmaps and frees the SMMU memory region a handle is mapped to + * @note Both `unmap_queue_lock` and `handle_description.mutex` MUST be locked when calling this + */ + void UnmapHandle(Handle& handle_description); + + /** + * @brief Removes a handle from the map taking its dupes into account + * @note handle_description.mutex MUST be locked when calling this + * @return If the handle was removed from the map + */ + bool TryRemoveHandle(const Handle& handle_description); +}; +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp new file mode 100644 index 000000000..eda2041a0 --- /dev/null +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp @@ -0,0 +1,121 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/assert.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" +#include "video_core/host1x/host1x.h" + +namespace Service::Nvidia::NvCore { + +SyncpointManager::SyncpointManager(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} { + constexpr u32 VBlank0SyncpointId{26}; + constexpr u32 VBlank1SyncpointId{27}; + + // Reserve both vblank syncpoints as client managed as they use Continuous Mode + // Refer to section 14.3.5.3 of the TRM for more information on Continuous Mode + // https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/drm/dc.c#L660 + ReserveSyncpoint(VBlank0SyncpointId, true); + ReserveSyncpoint(VBlank1SyncpointId, true); + + for (u32 syncpoint_id : channel_syncpoints) { + if (syncpoint_id) { + ReserveSyncpoint(syncpoint_id, false); + } + } +} + +SyncpointManager::~SyncpointManager() = default; + +u32 SyncpointManager::ReserveSyncpoint(u32 id, bool client_managed) { + if (syncpoints.at(id).reserved) { + ASSERT_MSG(false, "Requested syncpoint is in use"); + return 0; + } + + syncpoints.at(id).reserved = true; + syncpoints.at(id).interface_managed = client_managed; + + return id; +} + +u32 SyncpointManager::FindFreeSyncpoint() { + for (u32 i{1}; i < syncpoints.size(); i++) { + if (!syncpoints[i].reserved) { + return i; + } + } + ASSERT_MSG(false, "Failed to find a free syncpoint!"); + return 0; +} + +u32 SyncpointManager::AllocateSyncpoint(bool client_managed) { + std::lock_guard lock(reservation_lock); + return ReserveSyncpoint(FindFreeSyncpoint(), client_managed); +} + +void SyncpointManager::FreeSyncpoint(u32 id) { + std::lock_guard lock(reservation_lock); + ASSERT(syncpoints.at(id).reserved); + syncpoints.at(id).reserved = false; +} + +bool SyncpointManager::IsSyncpointAllocated(u32 id) { + return (id <= SyncpointCount) && syncpoints[id].reserved; +} + +bool SyncpointManager::HasSyncpointExpired(u32 id, u32 threshold) const { + const SyncpointInfo& syncpoint{syncpoints.at(id)}; + + if (!syncpoint.reserved) { + ASSERT(false); + return 0; + } + + // If the interface manages counters then we don't keep track of the maximum value as it handles + // sanity checking the values then + if (syncpoint.interface_managed) { + return static_cast<s32>(syncpoint.counter_min - threshold) >= 0; + } else { + return (syncpoint.counter_max - threshold) >= (syncpoint.counter_min - threshold); + } +} + +u32 SyncpointManager::IncrementSyncpointMaxExt(u32 id, u32 amount) { + if (!syncpoints.at(id).reserved) { + ASSERT(false); + return 0; + } + + return syncpoints.at(id).counter_max += amount; +} + +u32 SyncpointManager::ReadSyncpointMinValue(u32 id) { + if (!syncpoints.at(id).reserved) { + ASSERT(false); + return 0; + } + + return syncpoints.at(id).counter_min; +} + +u32 SyncpointManager::UpdateMin(u32 id) { + if (!syncpoints.at(id).reserved) { + ASSERT(false); + return 0; + } + + syncpoints.at(id).counter_min = host1x.GetSyncpointManager().GetHostSyncpointValue(id); + return syncpoints.at(id).counter_min; +} + +NvFence SyncpointManager::GetSyncpointFence(u32 id) { + if (!syncpoints.at(id).reserved) { + ASSERT(false); + return NvFence{}; + } + + return {.id = static_cast<s32>(id), .value = syncpoints.at(id).counter_max}; +} + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.h b/src/core/hle/service/nvdrv/core/syncpoint_manager.h new file mode 100644 index 000000000..b76ef9032 --- /dev/null +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.h @@ -0,0 +1,134 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <array> +#include <atomic> +#include <mutex> + +#include "common/common_types.h" +#include "core/hle/service/nvdrv/nvdata.h" + +namespace Tegra::Host1x { +class Host1x; +} // namespace Tegra::Host1x + +namespace Service::Nvidia::NvCore { + +enum class ChannelType : u32 { + MsEnc = 0, + VIC = 1, + GPU = 2, + NvDec = 3, + Display = 4, + NvJpg = 5, + TSec = 6, + Max = 7 +}; + +/** + * @brief SyncpointManager handles allocating and accessing host1x syncpoints, these are cached + * versions of the HW syncpoints which are intermittently synced + * @note Refer to Chapter 14 of the Tegra X1 TRM for an exhaustive overview of them + * @url https://http.download.nvidia.com/tegra-public-appnotes/host1x.html + * @url + * https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/jetson-tx1/drivers/video/tegra/host/nvhost_syncpt.c + */ +class SyncpointManager final { +public: + explicit SyncpointManager(Tegra::Host1x::Host1x& host1x); + ~SyncpointManager(); + + /** + * @brief Checks if the given syncpoint is both allocated and below the number of HW syncpoints + */ + bool IsSyncpointAllocated(u32 id); + + /** + * @brief Finds a free syncpoint and reserves it + * @return The ID of the reserved syncpoint + */ + u32 AllocateSyncpoint(bool client_managed); + + /** + * @url + * https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/syncpt.c#L259 + */ + bool HasSyncpointExpired(u32 id, u32 threshold) const; + + bool IsFenceSignalled(NvFence fence) const { + return HasSyncpointExpired(fence.id, fence.value); + } + + /** + * @brief Atomically increments the maximum value of a syncpoint by the given amount + * @return The new max value of the syncpoint + */ + u32 IncrementSyncpointMaxExt(u32 id, u32 amount); + + /** + * @return The minimum value of the syncpoint + */ + u32 ReadSyncpointMinValue(u32 id); + + /** + * @brief Synchronises the minimum value of the syncpoint to with the GPU + * @return The new minimum value of the syncpoint + */ + u32 UpdateMin(u32 id); + + /** + * @brief Frees the usage of a syncpoint. + */ + void FreeSyncpoint(u32 id); + + /** + * @return A fence that will be signalled once this syncpoint hits its maximum value + */ + NvFence GetSyncpointFence(u32 id); + + static constexpr std::array<u32, static_cast<u32>(ChannelType::Max)> channel_syncpoints{ + 0x0, // `MsEnc` is unimplemented + 0xC, // `VIC` + 0x0, // `GPU` syncpoints are allocated per-channel instead + 0x36, // `NvDec` + 0x0, // `Display` is unimplemented + 0x37, // `NvJpg` + 0x0, // `TSec` is unimplemented + }; //!< Maps each channel ID to a constant syncpoint + +private: + /** + * @note reservation_lock should be locked when calling this + */ + u32 ReserveSyncpoint(u32 id, bool client_managed); + + /** + * @return The ID of the first free syncpoint + */ + u32 FindFreeSyncpoint(); + + struct SyncpointInfo { + std::atomic<u32> counter_min; //!< The least value the syncpoint can be (The value it was + //!< when it was last synchronized with host1x) + std::atomic<u32> counter_max; //!< The maximum value the syncpoint can reach according to + //!< the current usage + bool interface_managed; //!< If the syncpoint is managed by a host1x client interface, a + //!< client interface is a HW block that can handle host1x + //!< transactions on behalf of a host1x client (Which would + //!< otherwise need to be manually synced using PIO which is + //!< synchronous and requires direct cooperation of the CPU) + bool reserved; //!< If the syncpoint is reserved or not, not to be confused with a reserved + //!< value + }; + + constexpr static std::size_t SyncpointCount{192}; + std::array<SyncpointInfo, SyncpointCount> syncpoints{}; + std::mutex reservation_lock; + + Tegra::Host1x::Host1x& host1x; +}; + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index 3d874243a..204b0e757 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -12,6 +11,10 @@ namespace Core { class System; } +namespace Kernel { +class KEvent; +} + namespace Service::Nvidia::Devices { /// Represents an abstract nvidia device node. It is to be subclassed by concrete device nodes to @@ -65,6 +68,10 @@ public: */ virtual void OnClose(DeviceFD fd) = 0; + virtual Kernel::KEvent* QueryEvent(u32 event_id) { + return nullptr; + } + protected: Core::System& system; }; diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 68f1e9060..4122fc98d 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -1,20 +1,20 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" #include "core/core_timing.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" -#include "core/hle/service/nvdrv/devices/nvmap.h" #include "core/perf_stats.h" #include "video_core/gpu.h" namespace Service::Nvidia::Devices { -nvdisp_disp0::nvdisp_disp0(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {} +nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core) + : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {} nvdisp_disp0::~nvdisp_disp0() = default; NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -38,23 +38,27 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& void nvdisp_disp0::OnOpen(DeviceFD fd) {} void nvdisp_disp0::OnClose(DeviceFD fd) {} -void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, - u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform, - const Common::Rectangle<int>& crop_rect) { - const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); +void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, + u32 height, u32 stride, android::BufferTransformFlags transform, + const Common::Rectangle<int>& crop_rect, + std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences) { + const VAddr addr = nvmap.GetHandleAddress(buffer_handle); LOG_TRACE(Service, "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", addr, offset, width, height, stride, format); - const auto pixel_format = static_cast<Tegra::FramebufferConfig::PixelFormat>(format); - const auto transform_flags = static_cast<Tegra::FramebufferConfig::TransformFlags>(transform); - const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, - stride, pixel_format, transform_flags, crop_rect}; + const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, + stride, format, transform, crop_rect}; + system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences); system.GetPerfStats().EndSystemFrame(); - system.GPU().SwapBuffers(&framebuffer); system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); system.GetPerfStats().BeginSystemFrame(); } +Kernel::KEvent* nvdisp_disp0::QueryEvent(u32 event_id) { + LOG_CRITICAL(Service_NVDRV, "Unknown DISP Event {}", event_id); + return nullptr; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index de01e1d5f..04217ab12 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -9,7 +8,13 @@ #include "common/common_types.h" #include "common/math_util.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" -#include "core/hle/service/nvflinger/buffer_queue.h" +#include "core/hle/service/nvflinger/buffer_transform_flags.h" +#include "core/hle/service/nvflinger/pixel_format.h" + +namespace Service::Nvidia::NvCore { +class Container; +class NvMap; +} // namespace Service::Nvidia::NvCore namespace Service::Nvidia::Devices { @@ -17,7 +22,7 @@ class nvmap; class nvdisp_disp0 final : public nvdevice { public: - explicit nvdisp_disp0(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_); + explicit nvdisp_disp0(Core::System& system_, NvCore::Container& core); ~nvdisp_disp0() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -31,12 +36,16 @@ public: void OnClose(DeviceFD fd) 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, - NVFlinger::BufferQueue::BufferTransformFlags transform, - const Common::Rectangle<int>& crop_rect); + void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, + u32 stride, android::BufferTransformFlags transform, + const Common::Rectangle<int>& crop_rect, + std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences); + + Kernel::KEvent* QueryEvent(u32 event_id) override; private: - std::shared_ptr<nvmap> nvmap_dev; + NvCore::Container& container; + NvCore::NvMap& nvmap; }; } // namespace Service::Nvidia::Devices 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 85170cdb3..6411dbf43 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -1,22 +1,30 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include <cstring> #include <utility> +#include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" -#include "core/hle/service/nvdrv/devices/nvmap.h" +#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" +#include "core/hle/service/nvdrv/nvdrv.h" +#include "video_core/control/channel_state.h" +#include "video_core/gpu.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" namespace Service::Nvidia::Devices { -nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {} +nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Container& core) + : nvdevice{system_}, module{module_}, container{core}, nvmap{core.GetNvMapFile()}, vm{}, + gmmu{} {} + nvhost_as_gpu::~nvhost_as_gpu() = default; NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -83,12 +91,52 @@ NvResult nvhost_as_gpu::AllocAsEx(const std::vector<u8>& input, std::vector<u8>& IoctlAllocAsEx params{}; std::memcpy(¶ms, input.data(), input.size()); - LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); - if (params.big_page_size == 0) { - params.big_page_size = DEFAULT_BIG_PAGE_SIZE; + LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size); + + std::scoped_lock lock(mutex); + + if (vm.initialised) { + ASSERT_MSG(false, "Cannot initialise an address space twice!"); + return NvResult::InvalidState; + } + + if (params.big_page_size) { + if (!std::has_single_bit(params.big_page_size)) { + LOG_ERROR(Service_NVDRV, "Non power-of-2 big page size: 0x{:X}!", params.big_page_size); + return NvResult::BadValue; + } + + if ((params.big_page_size & VM::SUPPORTED_BIG_PAGE_SIZES) == 0) { + LOG_ERROR(Service_NVDRV, "Unsupported big page size: 0x{:X}!", params.big_page_size); + return NvResult::BadValue; + } + + vm.big_page_size = params.big_page_size; + vm.big_page_size_bits = static_cast<u32>(std::countr_zero(params.big_page_size)); + + vm.va_range_start = params.big_page_size << VM::VA_START_SHIFT; + } + + // If this is unspecified then default values should be used + if (params.va_range_start) { + vm.va_range_start = params.va_range_start; + vm.va_range_split = params.va_range_split; + vm.va_range_end = params.va_range_end; } - big_page_size = params.big_page_size; + const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)}; + const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)}; + vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages); + + const auto start_big_pages{static_cast<u32>(vm.va_range_split >> vm.big_page_size_bits)}; + const auto end_big_pages{ + static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)}; + vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages); + + gmmu = std::make_shared<Tegra::MemoryManager>(system, 40, vm.big_page_size_bits, + VM::PAGE_SIZE_BITS); + system.GPU().InitAddressSpace(*gmmu); + vm.initialised = true; return NvResult::Success; } @@ -100,21 +148,76 @@ NvResult nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector< LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, params.page_size, params.flags); - const auto size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; - if ((params.flags & AddressSpaceFlags::FixedOffset) != AddressSpaceFlags::None) { - params.offset = *system.GPU().MemoryManager().AllocateFixed(params.offset, size); + std::scoped_lock lock(mutex); + + if (!vm.initialised) { + return NvResult::BadValue; + } + + if (params.page_size != VM::YUZU_PAGESIZE && params.page_size != vm.big_page_size) { + return NvResult::BadValue; + } + + if (params.page_size != vm.big_page_size && + ((params.flags & MappingFlags::Sparse) != MappingFlags::None)) { + UNIMPLEMENTED_MSG("Sparse small pages are not implemented!"); + return NvResult::NotImplemented; + } + + const u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS + : vm.big_page_size_bits}; + + auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator + : *vm.big_page_allocator}; + + if ((params.flags & MappingFlags::Fixed) != MappingFlags::None) { + allocator.AllocateFixed(static_cast<u32>(params.offset >> page_size_bits), params.pages); } else { - params.offset = system.GPU().MemoryManager().Allocate(size, params.align); + params.offset = static_cast<u64>(allocator.Allocate(params.pages)) << page_size_bits; + if (!params.offset) { + ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!"); + return NvResult::InsufficientMemory; + } } - auto result = NvResult::Success; - if (!params.offset) { - LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size); - result = NvResult::InsufficientMemory; + u64 size{static_cast<u64>(params.pages) * params.page_size}; + + if ((params.flags & MappingFlags::Sparse) != MappingFlags::None) { + gmmu->MapSparse(params.offset, size); } + allocation_map[params.offset] = { + .size = size, + .mappings{}, + .page_size = params.page_size, + .sparse = (params.flags & MappingFlags::Sparse) != MappingFlags::None, + .big_pages = params.page_size != VM::YUZU_PAGESIZE, + }; + std::memcpy(output.data(), ¶ms, output.size()); - return result; + return NvResult::Success; +} + +void nvhost_as_gpu::FreeMappingLocked(u64 offset) { + auto mapping{mapping_map.at(offset)}; + + if (!mapping->fixed) { + auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; + u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; + + allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits), + static_cast<u32>(mapping->size >> page_size_bits)); + } + + // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state + // Only FreeSpace can unmap them fully + if (mapping->sparse_alloc) { + gmmu->MapSparse(offset, mapping->size, mapping->big_page); + } else { + gmmu->Unmap(offset, mapping->size); + } + + mapping_map.erase(offset); } NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) { @@ -124,8 +227,40 @@ NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, params.pages, params.page_size); - system.GPU().MemoryManager().Unmap(params.offset, - static_cast<std::size_t>(params.pages) * params.page_size); + std::scoped_lock lock(mutex); + + if (!vm.initialised) { + return NvResult::BadValue; + } + + try { + auto allocation{allocation_map[params.offset]}; + + if (allocation.page_size != params.page_size || + allocation.size != (static_cast<u64>(params.pages) * params.page_size)) { + return NvResult::BadValue; + } + + for (const auto& mapping : allocation.mappings) { + FreeMappingLocked(mapping->offset); + } + + // Unset sparse flag if required + if (allocation.sparse) { + gmmu->Unmap(params.offset, allocation.size); + } + + auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator + : *vm.big_page_allocator}; + u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS + : vm.big_page_size_bits}; + + allocator.Free(static_cast<u32>(params.offset >> page_size_bits), + static_cast<u32>(allocation.size >> page_size_bits)); + allocation_map.erase(params.offset); + } catch (const std::out_of_range&) { + return NvResult::BadValue; + } std::memcpy(output.data(), ¶ms, output.size()); return NvResult::Success; @@ -136,35 +271,52 @@ NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& out LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); - auto result = NvResult::Success; std::vector<IoctlRemapEntry> entries(num_entries); std::memcpy(entries.data(), input.data(), input.size()); + std::scoped_lock lock(mutex); + + if (!vm.initialised) { + return NvResult::BadValue; + } + for (const auto& entry : entries) { - LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", - entry.offset, entry.nvmap_handle, entry.pages); + GPUVAddr virtual_address{static_cast<u64>(entry.as_offset_big_pages) + << vm.big_page_size_bits}; + u64 size{static_cast<u64>(entry.big_pages) << vm.big_page_size_bits}; - const auto object{nvmap_dev->GetObject(entry.nvmap_handle)}; - if (!object) { - LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle); - result = NvResult::InvalidState; - break; + auto alloc{allocation_map.upper_bound(virtual_address)}; + + if (alloc-- == allocation_map.begin() || + (virtual_address - alloc->first) + size > alloc->second.size) { + LOG_WARNING(Service_NVDRV, "Cannot remap into an unallocated region!"); + return NvResult::BadValue; } - const auto offset{static_cast<GPUVAddr>(entry.offset) << 0x10}; - const auto size{static_cast<u64>(entry.pages) << 0x10}; - const auto map_offset{static_cast<u64>(entry.map_offset) << 0x10}; - const auto addr{system.GPU().MemoryManager().Map(object->addr + map_offset, offset, size)}; + if (!alloc->second.sparse) { + LOG_WARNING(Service_NVDRV, "Cannot remap a non-sparse mapping!"); + return NvResult::BadValue; + } - if (!addr) { - LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!"); - result = NvResult::InvalidState; - break; + const bool use_big_pages = alloc->second.big_pages; + if (!entry.handle) { + gmmu->MapSparse(virtual_address, size, use_big_pages); + } else { + auto handle{nvmap.GetHandle(entry.handle)}; + if (!handle) { + return NvResult::BadValue; + } + + VAddr cpu_address{static_cast<VAddr>( + handle->address + + (static_cast<u64>(entry.handle_offset_big_pages) << vm.big_page_size_bits))}; + + gmmu->Map(virtual_address, cpu_address, size, use_big_pages); } } std::memcpy(output.data(), entries.data(), output.size()); - return result; + return NvResult::Success; } NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { @@ -174,79 +326,98 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8 LOG_DEBUG(Service_NVDRV, "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}" ", offset={}", - params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size, + params.flags, params.handle, params.buffer_offset, params.mapping_size, params.offset); - const auto object{nvmap_dev->GetObject(params.nvmap_handle)}; - if (!object) { - LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle); - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::InvalidState; - } - - // The real nvservices doesn't make a distinction between handles and ids, and - // object can only have one handle and it will be the same as its id. Assert that this is the - // case to prevent unexpected behavior. - ASSERT(object->id == params.nvmap_handle); - auto& gpu = system.GPU(); + std::scoped_lock lock(mutex); - u64 page_size{params.page_size}; - if (!page_size) { - page_size = object->align; + if (!vm.initialised) { + return NvResult::BadValue; } - if ((params.flags & AddressSpaceFlags::Remap) != AddressSpaceFlags::None) { - if (const auto buffer_map{FindBufferMap(params.offset)}; buffer_map) { - const auto cpu_addr{static_cast<VAddr>(buffer_map->CpuAddr() + params.buffer_offset)}; - const auto gpu_addr{static_cast<GPUVAddr>(params.offset + params.buffer_offset)}; + // Remaps a subregion of an existing mapping to a different PA + if ((params.flags & MappingFlags::Remap) != MappingFlags::None) { + try { + auto mapping{mapping_map.at(params.offset)}; - if (!gpu.MemoryManager().Map(cpu_addr, gpu_addr, params.mapping_size)) { - LOG_CRITICAL(Service_NVDRV, - "remap failed, flags={:X}, nvmap_handle={:X}, buffer_offset={}, " - "mapping_size = {}, offset={}", - params.flags, params.nvmap_handle, params.buffer_offset, - params.mapping_size, params.offset); - - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::InvalidState; + if (mapping->size < params.mapping_size) { + LOG_WARNING(Service_NVDRV, + "Cannot remap a partially mapped GPU address space region: 0x{:X}", + params.offset); + return NvResult::BadValue; } - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::Success; - } else { - LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset); + u64 gpu_address{static_cast<u64>(params.offset + params.buffer_offset)}; + VAddr cpu_address{mapping->ptr + params.buffer_offset}; + + gmmu->Map(gpu_address, cpu_address, params.mapping_size, mapping->big_page); - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::InvalidState; + return NvResult::Success; + } catch (const std::out_of_range&) { + LOG_WARNING(Service_NVDRV, "Cannot remap an unmapped GPU address space region: 0x{:X}", + params.offset); + return NvResult::BadValue; } } - // We can only map objects that have already been assigned a CPU address. - ASSERT(object->status == nvmap::Object::Status::Allocated); - - const auto physical_address{object->addr + params.buffer_offset}; - u64 size{params.mapping_size}; - if (!size) { - size = object->size; + auto handle{nvmap.GetHandle(params.handle)}; + if (!handle) { + return NvResult::BadValue; } - const bool is_alloc{(params.flags & AddressSpaceFlags::FixedOffset) == AddressSpaceFlags::None}; - if (is_alloc) { - params.offset = gpu.MemoryManager().MapAllocate(physical_address, size, page_size); - } else { - params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size); - } + VAddr cpu_address{static_cast<VAddr>(handle->address + params.buffer_offset)}; + u64 size{params.mapping_size ? params.mapping_size : handle->orig_size}; + + bool big_page{[&]() { + if (Common::IsAligned(handle->align, vm.big_page_size)) { + return true; + } else if (Common::IsAligned(handle->align, VM::YUZU_PAGESIZE)) { + return false; + } else { + ASSERT(false); + return false; + } + }()}; + + if ((params.flags & MappingFlags::Fixed) != MappingFlags::None) { + auto alloc{allocation_map.upper_bound(params.offset)}; - auto result = NvResult::Success; - if (!params.offset) { - LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size); - result = NvResult::InvalidState; + if (alloc-- == allocation_map.begin() || + (params.offset - alloc->first) + size > alloc->second.size) { + ASSERT_MSG(false, "Cannot perform a fixed mapping into an unallocated region!"); + return NvResult::BadValue; + } + + const bool use_big_pages = alloc->second.big_pages && big_page; + gmmu->Map(params.offset, cpu_address, size, use_big_pages); + + auto mapping{std::make_shared<Mapping>(cpu_address, params.offset, size, true, + use_big_pages, alloc->second.sparse)}; + alloc->second.mappings.push_back(mapping); + mapping_map[params.offset] = mapping; } else { - AddBufferMap(params.offset, size, physical_address, is_alloc); + + auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; + u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE}; + u32 page_size_bits{big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; + + params.offset = static_cast<u64>(allocator.Allocate( + static_cast<u32>(Common::AlignUp(size, page_size) >> page_size_bits))) + << page_size_bits; + if (!params.offset) { + ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!"); + return NvResult::InsufficientMemory; + } + + gmmu->Map(params.offset, cpu_address, Common::AlignUp(size, page_size), big_page); + + auto mapping{ + std::make_shared<Mapping>(cpu_address, params.offset, size, false, big_page, false)}; + mapping_map[params.offset] = mapping; } std::memcpy(output.data(), ¶ms, output.size()); - return result; + return NvResult::Success; } NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { @@ -255,47 +426,82 @@ NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); - if (const auto size{RemoveBufferMap(params.offset)}; size) { - system.GPU().MemoryManager().Unmap(params.offset, *size); - } else { - LOG_ERROR(Service_NVDRV, "invalid offset=0x{:X}", params.offset); + std::scoped_lock lock(mutex); + + if (!vm.initialised) { + return NvResult::BadValue; + } + + try { + auto mapping{mapping_map.at(params.offset)}; + + if (!mapping->fixed) { + auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; + u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; + + allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits), + static_cast<u32>(mapping->size >> page_size_bits)); + } + + // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state + // Only FreeSpace can unmap them fully + if (mapping->sparse_alloc) { + gmmu->MapSparse(params.offset, mapping->size, mapping->big_page); + } else { + gmmu->Unmap(params.offset, mapping->size); + } + + mapping_map.erase(params.offset); + } catch (const std::out_of_range&) { + LOG_WARNING(Service_NVDRV, "Couldn't find region to unmap at 0x{:X}", params.offset); } - std::memcpy(output.data(), ¶ms, output.size()); return NvResult::Success; } NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { IoctlBindChannel params{}; std::memcpy(¶ms, input.data(), input.size()); - LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}", params.fd); + LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); - channel = params.fd; + auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd); + gpu_channel_device->channel_state->memory_manager = gmmu; return NvResult::Success; } +void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) { + params.buf_size = 2 * sizeof(VaRegion); + + params.regions = std::array<VaRegion, 2>{ + VaRegion{ + .offset = vm.small_page_allocator->GetVAStart() << VM::PAGE_SIZE_BITS, + .page_size = VM::YUZU_PAGESIZE, + ._pad0_{}, + .pages = vm.small_page_allocator->GetVALimit() - vm.small_page_allocator->GetVAStart(), + }, + VaRegion{ + .offset = vm.big_page_allocator->GetVAStart() << vm.big_page_size_bits, + .page_size = vm.big_page_size, + ._pad0_{}, + .pages = vm.big_page_allocator->GetVALimit() - vm.big_page_allocator->GetVAStart(), + }, + }; +} + NvResult 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={:X}, buf_size={:X}", params.buf_addr, - params.buf_size); - - params.buf_size = 0x30; + LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, + params.buf_size); - params.small = IoctlVaRegion{ - .offset = 0x04000000, - .page_size = DEFAULT_SMALL_PAGE_SIZE, - .pages = 0x3fbfff, - }; + std::scoped_lock lock(mutex); - params.big = IoctlVaRegion{ - .offset = 0x04000000, - .page_size = big_page_size, - .pages = 0x1bffff, - }; + if (!vm.initialised) { + return NvResult::BadValue; + } - // TODO(ogniK): This probably can stay stubbed but should add support way way later + GetVARegionsImpl(params); std::memcpy(output.data(), ¶ms, output.size()); return NvResult::Success; @@ -306,62 +512,27 @@ NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u IoctlGetVaRegions params{}; std::memcpy(¶ms, input.data(), input.size()); - LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, - params.buf_size); - - params.buf_size = 0x30; + LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, + params.buf_size); - params.small = IoctlVaRegion{ - .offset = 0x04000000, - .page_size = 0x1000, - .pages = 0x3fbfff, - }; + std::scoped_lock lock(mutex); - params.big = IoctlVaRegion{ - .offset = 0x04000000, - .page_size = big_page_size, - .pages = 0x1bffff, - }; + if (!vm.initialised) { + return NvResult::BadValue; + } - // TODO(ogniK): This probably can stay stubbed but should add support way way later + GetVARegionsImpl(params); std::memcpy(output.data(), ¶ms, output.size()); - std::memcpy(inline_output.data(), ¶ms.small, sizeof(IoctlVaRegion)); - std::memcpy(inline_output.data() + sizeof(IoctlVaRegion), ¶ms.big, sizeof(IoctlVaRegion)); + std::memcpy(inline_output.data(), ¶ms.regions[0], sizeof(VaRegion)); + std::memcpy(inline_output.data() + sizeof(VaRegion), ¶ms.regions[1], sizeof(VaRegion)); return NvResult::Success; } -std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const { - const auto end{buffer_mappings.upper_bound(gpu_addr)}; - for (auto iter{buffer_mappings.begin()}; iter != end; ++iter) { - if (gpu_addr >= iter->second.StartAddr() && gpu_addr < iter->second.EndAddr()) { - return iter->second; - } - } - - return std::nullopt; -} - -void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, - bool is_allocated) { - buffer_mappings[gpu_addr] = {gpu_addr, size, cpu_addr, is_allocated}; -} - -std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) { - if (const auto iter{buffer_mappings.find(gpu_addr)}; iter != buffer_mappings.end()) { - std::size_t size{}; - - if (iter->second.IsAllocated()) { - size = iter->second.Size(); - } - - buffer_mappings.erase(iter); - - return size; - } - - return std::nullopt; +Kernel::KEvent* nvhost_as_gpu::QueryEvent(u32 event_id) { + LOG_CRITICAL(Service_NVDRV, "Unknown AS GPU Event {}", event_id); + return nullptr; } } // namespace Service::Nvidia::Devices 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 24e3151cb..86fe71c75 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -1,36 +1,50 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once +#include <bit> +#include <list> #include <map> #include <memory> +#include <mutex> #include <optional> #include <vector> +#include "common/address_space.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" -namespace Service::Nvidia::Devices { +namespace Tegra { +class MemoryManager; +} // namespace Tegra + +namespace Service::Nvidia { +class Module; +} -constexpr u32 DEFAULT_BIG_PAGE_SIZE = 1 << 16; -constexpr u32 DEFAULT_SMALL_PAGE_SIZE = 1 << 12; +namespace Service::Nvidia::NvCore { +class Container; +class NvMap; +} // namespace Service::Nvidia::NvCore -class nvmap; +namespace Service::Nvidia::Devices { -enum class AddressSpaceFlags : u32 { - None = 0x0, - FixedOffset = 0x1, - Remap = 0x100, +enum class MappingFlags : u32 { + None = 0, + Fixed = 1 << 0, + Sparse = 1 << 1, + Remap = 1 << 8, }; -DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags); +DECLARE_ENUM_FLAG_OPERATORS(MappingFlags); class nvhost_as_gpu final : public nvdevice { public: - explicit nvhost_as_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_); + explicit nvhost_as_gpu(Core::System& system_, Module& module, NvCore::Container& core); ~nvhost_as_gpu() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -43,46 +57,17 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; -private: - class BufferMap final { - public: - constexpr BufferMap() = default; - - constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_) - : start_addr{start_addr_}, end_addr{start_addr_ + size_} {} - - constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_, VAddr cpu_addr_, - bool is_allocated_) - : start_addr{start_addr_}, end_addr{start_addr_ + size_}, cpu_addr{cpu_addr_}, - is_allocated{is_allocated_} {} - - constexpr VAddr StartAddr() const { - return start_addr; - } - - constexpr VAddr EndAddr() const { - return end_addr; - } - - constexpr std::size_t Size() const { - return end_addr - start_addr; - } - - constexpr VAddr CpuAddr() const { - return cpu_addr; - } - - constexpr bool IsAllocated() const { - return is_allocated; - } - - private: - GPUVAddr start_addr{}; - GPUVAddr end_addr{}; - VAddr cpu_addr{}; - bool is_allocated{}; + Kernel::KEvent* QueryEvent(u32 event_id) override; + + struct VaRegion { + u64 offset; + u32 page_size; + u32 _pad0_; + u64 pages; }; + static_assert(sizeof(VaRegion) == 0x18); +private: struct IoctlAllocAsEx { u32_le flags{}; // usually passes 1 s32_le as_fd{}; // ignored; passes 0 @@ -97,7 +82,7 @@ private: struct IoctlAllocSpace { u32_le pages{}; u32_le page_size{}; - AddressSpaceFlags flags{}; + MappingFlags flags{}; INSERT_PADDING_WORDS(1); union { u64_le offset; @@ -114,19 +99,19 @@ private: static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size"); struct IoctlRemapEntry { - u16_le flags{}; - u16_le kind{}; - u32_le nvmap_handle{}; - u32_le map_offset{}; - u32_le offset{}; - u32_le pages{}; + u16 flags; + u16 kind; + NvCore::NvMap::Handle::Id handle; + u32 handle_offset_big_pages; + u32 as_offset_big_pages; + u32 big_pages; }; static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); struct IoctlMapBufferEx { - AddressSpaceFlags flags{}; // bit0: fixed_offset, bit2: cacheable - u32_le kind{}; // -1 is default - u32_le nvmap_handle{}; + MappingFlags flags{}; // bit0: fixed_offset, bit2: cacheable + u32_le kind{}; // -1 is default + NvCore::NvMap::Handle::Id handle; u32_le page_size{}; // 0 means don't care s64_le buffer_offset{}; u64_le mapping_size{}; @@ -144,27 +129,15 @@ private: }; 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 small{}; - IoctlVaRegion big{}; + std::array<VaRegion, 2> regions{}; }; - static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, + static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2, "IoctlGetVaRegions is incorrect size"); - s32 channel{}; - u32 big_page_size{DEFAULT_BIG_PAGE_SIZE}; - NvResult AllocAsEx(const std::vector<u8>& input, std::vector<u8>& output); NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output); NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output); @@ -173,18 +146,75 @@ private: NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output); NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output); + void GetVARegionsImpl(IoctlGetVaRegions& params); NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output, std::vector<u8>& inline_output); - std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const; - void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated); - std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr); + void FreeMappingLocked(u64 offset); + + Module& module; + + NvCore::Container& container; + NvCore::NvMap& nvmap; - std::shared_ptr<nvmap> nvmap_dev; + struct Mapping { + VAddr ptr; + u64 offset; + u64 size; + bool fixed; + bool big_page; // Only valid if fixed == false + bool sparse_alloc; + + Mapping(VAddr ptr_, u64 offset_, u64 size_, bool fixed_, bool big_page_, bool sparse_alloc_) + : ptr(ptr_), offset(offset_), size(size_), fixed(fixed_), big_page(big_page_), + sparse_alloc(sparse_alloc_) {} + }; + + struct Allocation { + u64 size; + std::list<std::shared_ptr<Mapping>> mappings; + u32 page_size; + bool sparse; + bool big_pages; + }; - // This is expected to be ordered, therefore we must use a map, not unordered_map - std::map<GPUVAddr, BufferMap> buffer_mappings; + std::map<u64, std::shared_ptr<Mapping>> + mapping_map; //!< This maps the base addresses of mapped buffers to their total sizes and + //!< mapping type, this is needed as what was originally a single buffer may + //!< have been split into multiple GPU side buffers with the remap flag. + std::map<u64, Allocation> allocation_map; //!< Holds allocations created by AllocSpace from + //!< which fixed buffers can be mapped into + std::mutex mutex; //!< Locks all AS operations + + struct VM { + static constexpr u32 YUZU_PAGESIZE{0x1000}; + static constexpr u32 PAGE_SIZE_BITS{std::countr_zero(YUZU_PAGESIZE)}; + + static constexpr u32 SUPPORTED_BIG_PAGE_SIZES{0x30000}; + static constexpr u32 DEFAULT_BIG_PAGE_SIZE{0x20000}; + u32 big_page_size{DEFAULT_BIG_PAGE_SIZE}; + u32 big_page_size_bits{std::countr_zero(DEFAULT_BIG_PAGE_SIZE)}; + + static constexpr u32 VA_START_SHIFT{10}; + static constexpr u64 DEFAULT_VA_SPLIT{1ULL << 34}; + static constexpr u64 DEFAULT_VA_RANGE{1ULL << 37}; + u64 va_range_start{DEFAULT_BIG_PAGE_SIZE << VA_START_SHIFT}; + u64 va_range_split{DEFAULT_VA_SPLIT}; + u64 va_range_end{DEFAULT_VA_RANGE}; + + using Allocator = Common::FlatAllocator<u32, 0, 32>; + + std::unique_ptr<Allocator> big_page_allocator; + std::shared_ptr<Allocator> + small_page_allocator; //! Shared as this is also used by nvhost::GpuChannel + + bool initialised{}; + } vm; + std::shared_ptr<Tegra::MemoryManager> gmmu; + + // s32 channel{}; + // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE}; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index f9b82b504..5bee4a3d3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -1,25 +1,39 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later +#include <bit> #include <cstdlib> #include <cstring> +#include <fmt/format.h> #include "common/assert.h" #include "common/logging/log.h" +#include "common/scope_exit.h" #include "core/core.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_writable_event.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" #include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" namespace Service::Nvidia::Devices { nvhost_ctrl::nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, - SyncpointManager& syncpoint_manager_) - : nvdevice{system_}, events_interface{events_interface_}, syncpoint_manager{ - syncpoint_manager_} {} -nvhost_ctrl::~nvhost_ctrl() = default; + NvCore::Container& core_) + : nvdevice{system_}, events_interface{events_interface_}, core{core_}, + syncpoint_manager{core_.GetSyncpointManager()} {} + +nvhost_ctrl::~nvhost_ctrl() { + for (auto& event : events) { + if (!event.registered) { + continue; + } + events_interface.FreeEvent(event.kevent); + } +} NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { @@ -31,13 +45,15 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& case 0x1c: return IocCtrlClearEventWait(input, output); case 0x1d: - return IocCtrlEventWait(input, output, false); - case 0x1e: return IocCtrlEventWait(input, output, true); + case 0x1e: + return IocCtrlEventWait(input, output, false); case 0x1f: return IocCtrlEventRegister(input, output); case 0x20: return IocCtrlEventUnregister(input, output); + case 0x21: + return IocCtrlEventUnregisterBatch(input, output); } break; default: @@ -61,6 +77,7 @@ NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& } void nvhost_ctrl::OnOpen(DeviceFD fd) {} + void nvhost_ctrl::OnClose(DeviceFD fd) {} NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { @@ -72,116 +89,167 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector } NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, - bool is_async) { + bool is_allocation) { IocCtrlEventWaitParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}", - params.syncpt_id, params.threshold, params.timeout, is_async); + LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}", + params.fence.id, params.fence.value, params.timeout, is_allocation); - if (params.syncpt_id >= MaxSyncPoints) { - return NvResult::BadParameter; - } + bool must_unmark_fail = !is_allocation; + const u32 event_id = params.value.raw; + SCOPE_EXIT({ + std::memcpy(output.data(), ¶ms, sizeof(params)); + if (must_unmark_fail) { + events[event_id].fails = 0; + } + }); - u32 event_id = params.value & 0x00FF; + const u32 fence_id = static_cast<u32>(params.fence.id); - if (event_id >= MaxNvEvents) { - std::memcpy(output.data(), ¶ms, sizeof(params)); + if (fence_id >= MaxSyncPoints) { return NvResult::BadParameter; } - if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { - params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id); - std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.failed[event_id] = false; + if (params.fence.value == 0) { + if (!syncpoint_manager.IsSyncpointAllocated(params.fence.id)) { + LOG_WARNING(Service_NVDRV, + "Unallocated syncpt_id={}, threshold={}, timeout={}, is_allocation={}", + params.fence.id, params.fence.value, params.timeout, is_allocation); + } else { + params.value.raw = syncpoint_manager.ReadSyncpointMinValue(fence_id); + } return NvResult::Success; } - if (const auto new_value = syncpoint_manager.RefreshSyncpoint(params.syncpt_id); - syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { - params.value = new_value; - std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.failed[event_id] = false; + if (syncpoint_manager.IsFenceSignalled(params.fence)) { + params.value.raw = syncpoint_manager.ReadSyncpointMinValue(fence_id); return NvResult::Success; } - auto& event = events_interface.events[event_id]; - auto& gpu = system.GPU(); - - // This is mostly to take into account unimplemented features. As synced - // gpu is always synced. - if (!gpu.IsAsync()) { - event.event->GetWritableEvent().Signal(); - return NvResult::Success; - } - const u32 current_syncpoint_value = event.fence.value; - const s32 diff = current_syncpoint_value - params.threshold; - if (diff >= 0) { - event.event->GetWritableEvent().Signal(); - params.value = current_syncpoint_value; - std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.failed[event_id] = false; + if (const auto new_value = syncpoint_manager.UpdateMin(fence_id); + syncpoint_manager.IsFenceSignalled(params.fence)) { + params.value.raw = new_value; return NvResult::Success; } - const u32 target_value = current_syncpoint_value - diff; - if (!is_async) { - params.value = 0; + auto& host1x_syncpoint_manager = system.Host1x().GetSyncpointManager(); + const u32 target_value = params.fence.value; + + auto lock = NvEventsLock(); + + u32 slot = [&]() { + if (is_allocation) { + params.value.raw = 0; + return FindFreeNvEvent(fence_id); + } else { + return params.value.raw; + } + }(); + + must_unmark_fail = false; + + const auto check_failing = [&]() { + if (events[slot].fails > 2) { + { + auto lk = system.StallProcesses(); + host1x_syncpoint_manager.WaitHost(fence_id, target_value); + system.UnstallProcesses(); + } + params.value.raw = target_value; + return true; + } + return false; + }; + + if (slot >= MaxNvEvents) { + return NvResult::BadParameter; } if (params.timeout == 0) { - std::memcpy(output.data(), ¶ms, sizeof(params)); + if (check_failing()) { + events[slot].fails = 0; + return NvResult::Success; + } return NvResult::Timeout; } - EventState status = events_interface.status[event_id]; - const bool bad_parameter = status != EventState::Free && status != EventState::Registered; - if (bad_parameter) { - std::memcpy(output.data(), ¶ms, sizeof(params)); + auto& event = events[slot]; + + if (!event.registered) { return NvResult::BadParameter; } - events_interface.SetEventStatus(event_id, EventState::Waiting); - events_interface.assigned_syncpt[event_id] = params.syncpt_id; - events_interface.assigned_value[event_id] = target_value; - if (is_async) { - params.value = params.syncpt_id << 4; - } else { - params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000; - } - params.value |= event_id; - event.event->GetWritableEvent().Clear(); - if (events_interface.failed[event_id]) { - { - auto lk = system.StallCPU(); - gpu.WaitFence(params.syncpt_id, target_value); - system.UnstallCPU(); - } - std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.failed[event_id] = false; + + if (event.IsBeingUsed()) { + return NvResult::BadParameter; + } + + if (check_failing()) { + event.fails = 0; return NvResult::Success; } - gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); - std::memcpy(output.data(), ¶ms, sizeof(params)); + + params.value.raw = 0; + + event.status.store(EventState::Waiting, std::memory_order_release); + event.assigned_syncpt = fence_id; + event.assigned_value = target_value; + if (is_allocation) { + params.value.syncpoint_id_for_allocation.Assign(static_cast<u16>(fence_id)); + params.value.event_allocated.Assign(1); + } else { + params.value.syncpoint_id.Assign(fence_id); + } + params.value.raw |= slot; + + event.wait_handle = + host1x_syncpoint_manager.RegisterHostAction(fence_id, target_value, [this, slot]() { + auto& event_ = events[slot]; + if (event_.status.exchange(EventState::Signalling, std::memory_order_acq_rel) == + EventState::Waiting) { + event_.kevent->GetWritableEvent().Signal(); + } + event_.status.store(EventState::Signalled, std::memory_order_release); + }); return NvResult::Timeout; } +NvResult nvhost_ctrl::FreeEvent(u32 slot) { + if (slot >= MaxNvEvents) { + return NvResult::BadParameter; + } + + auto& event = events[slot]; + + if (!event.registered) { + return NvResult::Success; + } + + if (event.IsBeingUsed()) { + return NvResult::Busy; + } + + FreeNvEvent(slot); + return NvResult::Success; +} + NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { IocCtrlEventRegisterParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); - const u32 event_id = params.user_event_id & 0x00FF; + const u32 event_id = params.user_event_id; LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); if (event_id >= MaxNvEvents) { return NvResult::BadParameter; } - if (events_interface.registered[event_id]) { - const auto event_state = events_interface.status[event_id]; - if (event_state != EventState::Free) { - LOG_WARNING(Service_NVDRV, "Event already registered! Unregistering previous event"); - events_interface.UnregisterEvent(event_id); - } else { - return NvResult::BadParameter; + + auto lock = NvEventsLock(); + + if (events[event_id].registered) { + const auto result = FreeEvent(event_id); + if (result != NvResult::Success) { + return result; } } - events_interface.RegisterEvent(event_id); + CreateNvEvent(event_id); return NvResult::Success; } @@ -191,34 +259,142 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::memcpy(¶ms, input.data(), sizeof(params)); const u32 event_id = params.user_event_id & 0x00FF; LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); - if (event_id >= MaxNvEvents) { - return NvResult::BadParameter; - } - if (!events_interface.registered[event_id]) { - return NvResult::BadParameter; + + auto lock = NvEventsLock(); + return FreeEvent(event_id); +} + +NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(const std::vector<u8>& input, + std::vector<u8>& output) { + IocCtrlEventUnregisterBatchParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + u64 event_mask = params.user_events; + LOG_DEBUG(Service_NVDRV, " called, event_mask: {:X}", event_mask); + + auto lock = NvEventsLock(); + while (event_mask != 0) { + const u64 event_id = std::countr_zero(event_mask); + event_mask &= ~(1ULL << event_id); + const auto result = FreeEvent(static_cast<u32>(event_id)); + if (result != NvResult::Success) { + return result; + } } - events_interface.UnregisterEvent(event_id); return NvResult::Success; } NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { - IocCtrlEventSignalParams params{}; + IocCtrlEventClearParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); - u32 event_id = params.event_id & 0x00FF; - LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id); + u32 event_id = params.event_id.slot; + LOG_DEBUG(Service_NVDRV, "called, event_id: {:X}", event_id); if (event_id >= MaxNvEvents) { return NvResult::BadParameter; } - if (events_interface.status[event_id] == EventState::Waiting) { - events_interface.LiberateEvent(event_id); - } - events_interface.failed[event_id] = true; - syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id); + auto lock = NvEventsLock(); + + auto& event = events[event_id]; + if (event.status.exchange(EventState::Cancelling, std::memory_order_acq_rel) == + EventState::Waiting) { + auto& host1x_syncpoint_manager = system.Host1x().GetSyncpointManager(); + host1x_syncpoint_manager.DeregisterHostAction(event.assigned_syncpt, event.wait_handle); + syncpoint_manager.UpdateMin(event.assigned_syncpt); + event.wait_handle = {}; + } + event.fails++; + event.status.store(EventState::Cancelled, std::memory_order_release); + event.kevent->GetWritableEvent().Clear(); return NvResult::Success; } +Kernel::KEvent* nvhost_ctrl::QueryEvent(u32 event_id) { + const auto desired_event = SyncpointEventValue{.raw = event_id}; + + const bool allocated = desired_event.event_allocated.Value() != 0; + const u32 slot{allocated ? desired_event.partial_slot.Value() + : static_cast<u32>(desired_event.slot)}; + if (slot >= MaxNvEvents) { + ASSERT(false); + return nullptr; + } + + const u32 syncpoint_id{allocated ? desired_event.syncpoint_id_for_allocation.Value() + : desired_event.syncpoint_id.Value()}; + + auto lock = NvEventsLock(); + + auto& event = events[slot]; + if (event.registered && event.assigned_syncpt == syncpoint_id) { + ASSERT(event.kevent); + return event.kevent; + } + // Is this possible in hardware? + ASSERT_MSG(false, "Slot:{}, SyncpointID:{}, requested", slot, syncpoint_id); + return nullptr; +} + +std::unique_lock<std::mutex> nvhost_ctrl::NvEventsLock() { + return std::unique_lock<std::mutex>(events_mutex); +} + +void nvhost_ctrl::CreateNvEvent(u32 event_id) { + auto& event = events[event_id]; + ASSERT(!event.kevent); + ASSERT(!event.registered); + ASSERT(!event.IsBeingUsed()); + event.kevent = events_interface.CreateEvent(fmt::format("NVCTRL::NvEvent_{}", event_id)); + event.status = EventState::Available; + event.registered = true; + const u64 mask = 1ULL << event_id; + event.fails = 0; + events_mask |= mask; + event.assigned_syncpt = 0; +} + +void nvhost_ctrl::FreeNvEvent(u32 event_id) { + auto& event = events[event_id]; + ASSERT(event.kevent); + ASSERT(event.registered); + ASSERT(!event.IsBeingUsed()); + events_interface.FreeEvent(event.kevent); + event.kevent = nullptr; + event.status = EventState::Available; + event.registered = false; + const u64 mask = ~(1ULL << event_id); + events_mask &= mask; +} + +u32 nvhost_ctrl::FindFreeNvEvent(u32 syncpoint_id) { + u32 slot{MaxNvEvents}; + u32 free_slot{MaxNvEvents}; + for (u32 i = 0; i < MaxNvEvents; i++) { + auto& event = events[i]; + if (event.registered) { + if (!event.IsBeingUsed()) { + slot = i; + if (event.assigned_syncpt == syncpoint_id) { + return slot; + } + } + } else if (free_slot == MaxNvEvents) { + free_slot = i; + } + } + if (free_slot < MaxNvEvents) { + CreateNvEvent(free_slot); + return free_slot; + } + + if (slot < MaxNvEvents) { + return slot; + } + + LOG_CRITICAL(Service_NVDRV, "Failed to allocate an event"); + return 0; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index cdf03887d..0b56d7070 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -1,21 +1,28 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once #include <array> #include <vector> +#include "common/bit_field.h" #include "common/common_types.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/nvdrv.h" +#include "video_core/host1x/syncpoint_manager.h" + +namespace Service::Nvidia::NvCore { +class Container; +class SyncpointManager; +} // namespace Service::Nvidia::NvCore namespace Service::Nvidia::Devices { class nvhost_ctrl final : public nvdevice { public: explicit nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, - SyncpointManager& syncpoint_manager_); + NvCore::Container& core); ~nvhost_ctrl() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -28,7 +35,70 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; + Kernel::KEvent* QueryEvent(u32 event_id) override; + + union SyncpointEventValue { + u32 raw; + + union { + BitField<0, 4, u32> partial_slot; + BitField<4, 28, u32> syncpoint_id; + }; + + struct { + u16 slot; + union { + BitField<0, 12, u16> syncpoint_id_for_allocation; + BitField<12, 1, u16> event_allocated; + }; + }; + }; + static_assert(sizeof(SyncpointEventValue) == sizeof(u32)); + private: + struct InternalEvent { + // Mask representing registered events + + // Each kernel event associated to an NV event + Kernel::KEvent* kevent{}; + // The status of the current NVEvent + std::atomic<EventState> status{}; + + // Tells the NVEvent that it has failed. + u32 fails{}; + // When an NVEvent is waiting on GPU interrupt, this is the sync_point + // associated with it. + u32 assigned_syncpt{}; + // This is the value of the GPU interrupt for which the NVEvent is waiting + // for. + u32 assigned_value{}; + + // Tells if an NVEvent is registered or not + bool registered{}; + + // Used for waiting on a syncpoint & canceling it. + Tegra::Host1x::SyncpointManager::ActionHandle wait_handle{}; + + bool IsBeingUsed() const { + const auto current_status = status.load(std::memory_order_acquire); + return current_status == EventState::Waiting || + current_status == EventState::Cancelling || + current_status == EventState::Signalling; + } + }; + + std::unique_lock<std::mutex> NvEventsLock(); + + void CreateNvEvent(u32 event_id); + + void FreeNvEvent(u32 event_id); + + u32 FindFreeNvEvent(u32 syncpoint_id); + + std::array<InternalEvent, MaxNvEvents> events{}; + std::mutex events_mutex; + u64 events_mask{}; + struct IocSyncptReadParams { u32_le id{}; u32_le value{}; @@ -84,27 +154,18 @@ private: }; static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); - struct IocCtrlEventSignalParams { - u32_le event_id{}; + struct IocCtrlEventClearParams { + SyncpointEventValue event_id{}; }; - static_assert(sizeof(IocCtrlEventSignalParams) == 4, - "IocCtrlEventSignalParams is incorrect size"); + static_assert(sizeof(IocCtrlEventClearParams) == 4, + "IocCtrlEventClearParams 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"); - - struct IocCtrlEventWaitAsyncParams { - u32_le syncpt_id{}; - u32_le threshold{}; + NvFence fence{}; u32_le timeout{}; - u32_le value{}; + SyncpointEventValue value{}; }; - static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16, + static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitAsyncParams is incorrect size"); struct IocCtrlEventRegisterParams { @@ -119,19 +180,25 @@ private: static_assert(sizeof(IocCtrlEventUnregisterParams) == 4, "IocCtrlEventUnregisterParams is incorrect size"); - struct IocCtrlEventKill { + struct IocCtrlEventUnregisterBatchParams { u64_le user_events{}; }; - static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size"); + static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8, + "IocCtrlEventKill is incorrect size"); NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); - NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async); + NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, + bool is_allocation); NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); + NvResult IocCtrlEventUnregisterBatch(const std::vector<u8>& input, std::vector<u8>& output); NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output); + NvResult FreeEvent(u32 slot); + EventInterface& events_interface; - SyncpointManager& syncpoint_manager; + NvCore::Container& core; + NvCore::SyncpointManager& syncpoint_manager; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 05b4e2151..ced57dfe6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include "common/assert.h" @@ -8,11 +7,19 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" +#include "core/hle/service/nvdrv/nvdrv.h" namespace Service::Nvidia::Devices { -nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_) : nvdevice{system_} {} -nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; +nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_) + : nvdevice{system_}, events_interface{events_interface_} { + error_notifier_event = events_interface.CreateEvent("CtrlGpuErrorNotifier"); + unknown_event = events_interface.CreateEvent("CtrlGpuUknownEvent"); +} +nvhost_ctrl_gpu::~nvhost_ctrl_gpu() { + events_interface.FreeEvent(error_notifier_event); + events_interface.FreeEvent(unknown_event); +} NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { @@ -287,4 +294,17 @@ NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u return NvResult::Success; } +Kernel::KEvent* nvhost_ctrl_gpu::QueryEvent(u32 event_id) { + switch (event_id) { + case 1: + return error_notifier_event; + case 2: + return unknown_event; + default: { + LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); + } + } + return nullptr; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index 898d00a17..1e8f254e2 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -1,19 +1,24 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <vector> + +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" +namespace Service::Nvidia { +class EventInterface; +} + namespace Service::Nvidia::Devices { class nvhost_ctrl_gpu final : public nvdevice { public: - explicit nvhost_ctrl_gpu(Core::System& system_); + explicit nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_); ~nvhost_ctrl_gpu() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -26,6 +31,8 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; + Kernel::KEvent* QueryEvent(u32 event_id) override; + private: struct IoctlGpuCharacteristics { u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200) @@ -159,6 +166,12 @@ private: NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output); NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output); + + EventInterface& events_interface; + + // Events + Kernel::KEvent* error_notifier_event; + Kernel::KEvent* unknown_event; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 0a043e386..45a759fa8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -1,34 +1,50 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" +#include "core/hle/service/nvdrv/nvdrv.h" #include "core/memory.h" +#include "video_core/control/channel_state.h" +#include "video_core/engines/puller.h" #include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" namespace Service::Nvidia::Devices { namespace { -Tegra::CommandHeader BuildFenceAction(Tegra::GPU::FenceOperation op, u32 syncpoint_id) { - Tegra::GPU::FenceAction result{}; +Tegra::CommandHeader BuildFenceAction(Tegra::Engines::Puller::FenceOperation op, u32 syncpoint_id) { + Tegra::Engines::Puller::FenceAction result{}; result.op.Assign(op); result.syncpoint_id.Assign(syncpoint_id); return {result.raw}; } } // namespace -nvhost_gpu::nvhost_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, - SyncpointManager& syncpoint_manager_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, syncpoint_manager{syncpoint_manager_} { - channel_fence.id = syncpoint_manager_.AllocateSyncpoint(); - channel_fence.value = system_.GPU().GetSyncpointValue(channel_fence.id); +nvhost_gpu::nvhost_gpu(Core::System& system_, EventInterface& events_interface_, + NvCore::Container& core_) + : nvdevice{system_}, events_interface{events_interface_}, core{core_}, + syncpoint_manager{core_.GetSyncpointManager()}, nvmap{core.GetNvMapFile()}, + channel_state{system.GPU().AllocateChannel()} { + channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false); + sm_exception_breakpoint_int_report_event = + events_interface.CreateEvent("GpuChannelSMExceptionBreakpointInt"); + sm_exception_breakpoint_pause_report_event = + events_interface.CreateEvent("GpuChannelSMExceptionBreakpointPause"); + error_notifier_event = events_interface.CreateEvent("GpuChannelErrorNotifier"); } -nvhost_gpu::~nvhost_gpu() = default; +nvhost_gpu::~nvhost_gpu() { + events_interface.FreeEvent(sm_exception_breakpoint_int_report_event); + events_interface.FreeEvent(sm_exception_breakpoint_pause_report_event); + events_interface.FreeEvent(error_notifier_event); + syncpoint_manager.FreeSyncpoint(channel_syncpoint); +} NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { @@ -168,9 +184,14 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8 params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, params.unk3); - channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id); + if (channel_state->initialized) { + LOG_CRITICAL(Service_NVDRV, "Already allocated!"); + return NvResult::AlreadyAllocated; + } + + system.GPU().InitChannel(*channel_state); - params.fence_out = channel_fence; + params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint); std::memcpy(output.data(), ¶ms, output.size()); return NvResult::Success; @@ -187,40 +208,39 @@ NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::ve return NvResult::Success; } -static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) { +static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) { return { - Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, + Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, Tegra::SubmissionMode::Increasing), {fence.value}, - Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, + Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, Tegra::SubmissionMode::Increasing), - BuildFenceAction(Tegra::GPU::FenceOperation::Acquire, fence.id), + BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Acquire, fence.id), }; } -static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, u32 add_increment) { +static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence) { std::vector<Tegra::CommandHeader> result{ - Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, + Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, Tegra::SubmissionMode::Increasing), {}}; - for (u32 count = 0; count < add_increment; ++count) { - result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, + for (u32 count = 0; count < 2; ++count) { + result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, Tegra::SubmissionMode::Increasing)); - result.emplace_back(BuildFenceAction(Tegra::GPU::FenceOperation::Increment, fence.id)); + result.emplace_back( + BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Increment, fence.id)); } return result; } -static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence fence, - u32 add_increment) { +static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence) { std::vector<Tegra::CommandHeader> result{ - Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1, + Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1, Tegra::SubmissionMode::Increasing), {}}; - const std::vector<Tegra::CommandHeader> increment{ - BuildIncrementCommandList(fence, add_increment)}; + const std::vector<Tegra::CommandHeader> increment{BuildIncrementCommandList(fence)}; result.insert(result.end(), increment.begin(), increment.end()); @@ -234,33 +254,41 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8> auto& gpu = system.GPU(); - params.fence_out.id = channel_fence.id; + std::scoped_lock lock(channel_mutex); - if (params.flags.add_wait.Value() && - !syncpoint_manager.IsSyncpointExpired(params.fence_out.id, params.fence_out.value)) { - gpu.PushGPUEntries(Tegra::CommandList{BuildWaitCommandList(params.fence_out)}); - } + const auto bind_id = channel_state->bind_id; - if (params.flags.add_increment.Value() || params.flags.increment.Value()) { - const u32 increment_value = params.flags.increment.Value() ? params.fence_out.value : 0; - params.fence_out.value = syncpoint_manager.IncreaseSyncpoint( - params.fence_out.id, params.AddIncrementValue() + increment_value); - } else { - params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id); + auto& flags = params.flags; + + if (flags.fence_wait.Value()) { + if (flags.increment_value.Value()) { + return NvResult::BadParameter; + } + + if (!syncpoint_manager.IsFenceSignalled(params.fence)) { + gpu.PushGPUEntries(bind_id, Tegra::CommandList{BuildWaitCommandList(params.fence)}); + } } - gpu.PushGPUEntries(std::move(entries)); + params.fence.id = channel_syncpoint; + + u32 increment{(flags.fence_increment.Value() != 0 ? 2 : 0) + + (flags.increment_value.Value() != 0 ? params.fence.value : 0)}; + params.fence.value = syncpoint_manager.IncrementSyncpointMaxExt(channel_syncpoint, increment); + gpu.PushGPUEntries(bind_id, std::move(entries)); - if (params.flags.add_increment.Value()) { - if (params.flags.suppress_wfi) { - gpu.PushGPUEntries(Tegra::CommandList{ - BuildIncrementCommandList(params.fence_out, params.AddIncrementValue())}); + if (flags.fence_increment.Value()) { + if (flags.suppress_wfi.Value()) { + gpu.PushGPUEntries(bind_id, + Tegra::CommandList{BuildIncrementCommandList(params.fence)}); } else { - gpu.PushGPUEntries(Tegra::CommandList{ - BuildIncrementWithWfiCommandList(params.fence_out, params.AddIncrementValue())}); + gpu.PushGPUEntries(bind_id, + Tegra::CommandList{BuildIncrementWithWfiCommandList(params.fence)}); } } + flags.raw = 0; + std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo)); return NvResult::Success; } @@ -328,4 +356,19 @@ NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vect return NvResult::Success; } +Kernel::KEvent* nvhost_gpu::QueryEvent(u32 event_id) { + switch (event_id) { + case 1: + return sm_exception_breakpoint_int_report_event; + case 2: + return sm_exception_breakpoint_pause_report_event; + case 3: + return error_notifier_event; + default: { + LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); + } + } + return nullptr; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index f27a82bff..1e4ecd55b 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -1,29 +1,43 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <memory> #include <vector> #include "common/bit_field.h" +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/nvdata.h" #include "video_core/dma_pusher.h" +namespace Tegra { +namespace Control { +struct ChannelState; +} +} // namespace Tegra + namespace Service::Nvidia { + +namespace NvCore { +class Container; +class NvMap; class SyncpointManager; -} +} // namespace NvCore + +class EventInterface; +} // namespace Service::Nvidia namespace Service::Nvidia::Devices { +class nvhost_as_gpu; class nvmap; class nvhost_gpu final : public nvdevice { public: - explicit nvhost_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, - SyncpointManager& syncpoint_manager_); + explicit nvhost_gpu(Core::System& system_, EventInterface& events_interface_, + NvCore::Container& core); ~nvhost_gpu() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -36,7 +50,10 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; + Kernel::KEvent* QueryEvent(u32 event_id) override; + private: + friend class nvhost_as_gpu; enum class CtxObjects : u32_le { Ctx2D = 0x902D, Ctx3D = 0xB197, @@ -108,7 +125,7 @@ private: static_assert(sizeof(IoctlGetErrorNotification) == 16, "IoctlGetErrorNotification is incorrect size"); - static_assert(sizeof(Fence) == 8, "Fence is incorrect size"); + static_assert(sizeof(NvFence) == 8, "Fence is incorrect size"); struct IoctlAllocGpfifoEx { u32_le num_entries{}; @@ -126,7 +143,7 @@ private: u32_le num_entries{}; // in u32_le flags{}; // in u32_le unk0{}; // in (1 works) - Fence fence_out{}; // out + NvFence fence_out{}; // out u32_le unk1{}; // in u32_le unk2{}; // in u32_le unk3{}; // in @@ -146,19 +163,15 @@ private: u32_le num_entries{}; // number of fence objects being submitted union { u32_le raw; - BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list - BitField<1, 1, u32_le> add_increment; // append an increment to the list - BitField<2, 1, u32_le> new_hw_format; // mostly ignored - BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt - BitField<8, 1, u32_le> increment; // increment the returned fence + BitField<0, 1, u32_le> fence_wait; // append a wait sync_point to the list + BitField<1, 1, u32_le> fence_increment; // append an increment to the list + BitField<2, 1, u32_le> new_hw_format; // mostly ignored + BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt + BitField<8, 1, u32_le> increment_value; // increment the returned fence } flags; - Fence fence_out{}; // returned new fence object for others to wait on - - u32 AddIncrementValue() const { - return flags.add_increment.Value() << 1; - } + NvFence fence{}; // returned new fence object for others to wait on }; - static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence), + static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence), "IoctlSubmitGpfifo is incorrect size"); struct IoctlGetWaitbase { @@ -191,9 +204,18 @@ private: NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output); - std::shared_ptr<nvmap> nvmap_dev; - SyncpointManager& syncpoint_manager; - Fence channel_fence; + EventInterface& events_interface; + NvCore::Container& core; + NvCore::SyncpointManager& syncpoint_manager; + NvCore::NvMap& nvmap; + std::shared_ptr<Tegra::Control::ChannelState> channel_state; + u32 channel_syncpoint; + std::mutex channel_mutex; + + // Events + Kernel::KEvent* sm_exception_breakpoint_int_report_event; + Kernel::KEvent* sm_exception_breakpoint_pause_report_event; + Kernel::KEvent* error_notifier_event; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index 8314d1ec2..1703f9cc3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -1,18 +1,18 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#include "audio_core/audio_core.h" #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" #include "video_core/renderer_base.h" namespace Service::Nvidia::Devices { -nvhost_nvdec::nvhost_nvdec(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, - SyncpointManager& syncpoint_manager_) - : nvhost_nvdec_common{system_, std::move(nvmap_dev_), syncpoint_manager_} {} +nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core_) + : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::NvDec} {} nvhost_nvdec::~nvhost_nvdec() = default; NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -21,8 +21,9 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& case 0x0: switch (command.cmd) { case 0x1: { - if (!fd_to_id.contains(fd)) { - fd_to_id[fd] = next_id++; + auto& host1x_file = core.Host1xDeviceFile(); + if (!host1x_file.fd_to_id.contains(fd)) { + host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++; } return Submit(fd, input, output); } @@ -66,14 +67,19 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& return NvResult::NotImplemented; } -void nvhost_nvdec::OnOpen(DeviceFD fd) {} +void nvhost_nvdec::OnOpen(DeviceFD fd) { + LOG_INFO(Service_NVDRV, "NVDEC video stream started"); + system.AudioCore().SetNVDECActive(true); +} void nvhost_nvdec::OnClose(DeviceFD fd) { LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); - const auto iter = fd_to_id.find(fd); - if (iter != fd_to_id.end()) { + auto& host1x_file = core.Host1xDeviceFile(); + const auto iter = host1x_file.fd_to_id.find(fd); + if (iter != host1x_file.fd_to_id.end()) { system.GPU().ClearCdmaInstance(iter->second); } + system.AudioCore().SetNVDECActive(false); } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index a507c4d0a..c1b4e53e8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -11,8 +10,7 @@ namespace Service::Nvidia::Devices { class nvhost_nvdec final : public nvhost_nvdec_common { public: - explicit nvhost_nvdec(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, - SyncpointManager& syncpoint_manager_); + explicit nvhost_nvdec(Core::System& system_, NvCore::Container& core); ~nvhost_nvdec() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -24,9 +22,6 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; - -private: - u32 next_id{}; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index 8a05f0668..99eede702 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstring> @@ -9,10 +8,12 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" -#include "core/hle/service/nvdrv/devices/nvmap.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" #include "core/memory.h" +#include "video_core/host1x/host1x.h" #include "video_core/memory_manager.h" #include "video_core/renderer_base.h" @@ -45,10 +46,22 @@ std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::s } } // Anonymous namespace -nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, - SyncpointManager& syncpoint_manager_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, syncpoint_manager{syncpoint_manager_} {} -nvhost_nvdec_common::~nvhost_nvdec_common() = default; +nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_, + NvCore::ChannelType channel_type_) + : nvdevice{system_}, core{core_}, syncpoint_manager{core.GetSyncpointManager()}, + nvmap{core.GetNvMapFile()}, channel_type{channel_type_} { + auto& syncpts_accumulated = core.Host1xDeviceFile().syncpts_accumulated; + if (syncpts_accumulated.empty()) { + channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false); + } else { + channel_syncpoint = syncpts_accumulated.front(); + syncpts_accumulated.pop_front(); + } +} + +nvhost_nvdec_common::~nvhost_nvdec_common() { + core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint); +} NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { IoctlSetNvmapFD params{}; @@ -85,16 +98,16 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input, for (std::size_t i = 0; i < syncpt_increments.size(); i++) { const SyncptIncr& syncpt_incr = syncpt_increments[i]; fence_thresholds[i] = - syncpoint_manager.IncreaseSyncpoint(syncpt_incr.id, syncpt_incr.increments); + syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments); } } for (const auto& cmd_buffer : command_buffers) { - const auto object = nvmap_dev->GetObject(cmd_buffer.memory_id); + const auto object = nvmap.GetHandle(cmd_buffer.memory_id); ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); - system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(), + system.Memory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(), cmdlist.size() * sizeof(u32)); - gpu.PushCommandBuffer(fd_to_id[fd], cmdlist); + gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist); } std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit)); // Some games expect command_buffers to be written back @@ -113,10 +126,8 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint)); LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); - if (device_syncpoints[params.param] == 0 && system.GPU().UseNvdec()) { - device_syncpoints[params.param] = syncpoint_manager.AllocateSyncpoint(); - } - params.value = device_syncpoints[params.param]; + // const u32 id{NvCore::SyncpointManager::channel_syncpoints[static_cast<u32>(channel_type)]}; + params.value = channel_syncpoint; std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint)); return NvResult::Success; @@ -124,6 +135,7 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { IoctlGetWaitbase params{}; + LOG_CRITICAL(Service_NVDRV, "called WAITBASE"); std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); params.value = 0; // Seems to be hard coded at 0 std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase)); @@ -137,28 +149,8 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); - auto& gpu = system.GPU(); - for (auto& cmd_buffer : cmd_buffer_handles) { - auto object{nvmap_dev->GetObject(cmd_buffer.map_handle)}; - if (!object) { - LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle); - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::InvalidState; - } - if (object->dma_map_addr == 0) { - // NVDEC and VIC memory is in the 32-bit address space - // MapAllocate32 will attempt to map a lower 32-bit value in the shared gpu memory space - const GPUVAddr low_addr = gpu.MemoryManager().MapAllocate32(object->addr, object->size); - object->dma_map_addr = static_cast<u32>(low_addr); - // Ensure that the dma_map_addr is indeed in the lower 32-bit address space. - ASSERT(object->dma_map_addr == low_addr); - } - if (!object->dma_map_addr) { - LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size); - } else { - cmd_buffer.map_address = object->dma_map_addr; - } + cmd_buffer.map_address = nvmap.PinHandle(cmd_buffer.map_handle); } std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer)); std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(), @@ -168,11 +160,16 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto } NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { - // This is intntionally stubbed. - // Skip unmapping buffers here, as to not break the continuity of the VP9 reference frame - // addresses, and risk invalidating data before the async GPU thread is done with it + IoctlMapBuffer params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer)); + std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); + + SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); + for (auto& cmd_buffer : cmd_buffer_handles) { + nvmap.UnpinHandle(cmd_buffer.map_handle); + } + std::memset(output.data(), 0, output.size()); - LOG_DEBUG(Service_NVDRV, "(STUBBED) called"); return NvResult::Success; } @@ -183,4 +180,9 @@ NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input, return NvResult::Success; } +Kernel::KEvent* nvhost_nvdec_common::QueryEvent(u32 event_id) { + LOG_CRITICAL(Service_NVDRV, "Unknown HOSTX1 Event {}", event_id); + return nullptr; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index e28c54df6..fe76100c8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -1,24 +1,28 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include <deque> #include <vector> #include "common/common_types.h" #include "common/swap.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" namespace Service::Nvidia { -class SyncpointManager; + +namespace NvCore { +class Container; +class NvMap; +} // namespace NvCore namespace Devices { -class nvmap; class nvhost_nvdec_common : public nvdevice { public: - explicit nvhost_nvdec_common(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, - SyncpointManager& syncpoint_manager_); + explicit nvhost_nvdec_common(Core::System& system_, NvCore::Container& core, + NvCore::ChannelType channel_type); ~nvhost_nvdec_common() override; protected: @@ -111,11 +115,15 @@ protected: NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output); - std::unordered_map<DeviceFD, u32> fd_to_id{}; + Kernel::KEvent* QueryEvent(u32 event_id) override; + + u32 channel_syncpoint; s32_le nvmap_fd{}; u32_le submit_timeout{}; - std::shared_ptr<nvmap> nvmap_dev; - SyncpointManager& syncpoint_manager; + NvCore::Container& core; + NvCore::SyncpointManager& syncpoint_manager; + NvCore::NvMap& nvmap; + NvCore::ChannelType channel_type; std::array<u32, MaxSyncPoints> device_syncpoints{}; }; }; // namespace Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp index c2be3cea7..bdbc2f9e1 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h index 6045e5cbd..440e7d371 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index 76b39806f..73f97136e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -1,17 +1,17 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/devices/nvhost_vic.h" #include "video_core/renderer_base.h" namespace Service::Nvidia::Devices { -nvhost_vic::nvhost_vic(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, - SyncpointManager& syncpoint_manager_) - : nvhost_nvdec_common{system_, std::move(nvmap_dev_), syncpoint_manager_} {} + +nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core_) + : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::VIC} {} nvhost_vic::~nvhost_vic() = default; @@ -20,11 +20,13 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i switch (command.group) { case 0x0: switch (command.cmd) { - case 0x1: - if (!fd_to_id.contains(fd)) { - fd_to_id[fd] = next_id++; + case 0x1: { + auto& host1x_file = core.Host1xDeviceFile(); + if (!host1x_file.fd_to_id.contains(fd)) { + host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++; } return Submit(fd, input, output); + } case 0x2: return GetSyncpoint(input, output); case 0x3: @@ -68,8 +70,9 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& i void nvhost_vic::OnOpen(DeviceFD fd) {} void nvhost_vic::OnClose(DeviceFD fd) { - const auto iter = fd_to_id.find(fd); - if (iter != fd_to_id.end()) { + auto& host1x_file = core.Host1xDeviceFile(); + const auto iter = host1x_file.fd_to_id.find(fd); + if (iter != host1x_file.fd_to_id.end()) { system.GPU().ClearCdmaInstance(iter->second); } } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index c9732c037..f164caafb 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,8 +9,7 @@ namespace Service::Nvidia::Devices { class nvhost_vic final : public nvhost_nvdec_common { public: - explicit nvhost_vic(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, - SyncpointManager& syncpoint_manager_); + explicit nvhost_vic(Core::System& system_, NvCore::Container& core); ~nvhost_vic(); NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -23,8 +21,5 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; - -private: - u32 next_id{}; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index dc59b4494..ddf273b5e 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -1,21 +1,27 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> +#include <bit> #include <cstring> +#include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/kernel/k_page_table.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvmap.h" +#include "core/memory.h" + +using Core::Memory::YUZU_PAGESIZE; namespace Service::Nvidia::Devices { -nvmap::nvmap(Core::System& system_) : nvdevice{system_} { - // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to - // represent this. - CreateObject(0); -} +nvmap::nvmap(Core::System& system_, NvCore::Container& container_) + : nvdevice{system_}, container{container_}, file{container.GetNvMapFile()} {} nvmap::~nvmap() = default; @@ -63,39 +69,21 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, void nvmap::OnOpen(DeviceFD fd) {} void nvmap::OnClose(DeviceFD fd) {} -VAddr nvmap::GetObjectAddress(u32 handle) const { - auto object = GetObject(handle); - ASSERT(object); - ASSERT(object->status == Object::Status::Allocated); - return object->addr; -} - -u32 nvmap::CreateObject(u32 size) { - // Create a new nvmap object and obtain a handle to it. - auto object = std::make_shared<Object>(); - object->id = next_id++; - object->size = size; - object->status = Object::Status::Created; - object->refcount = 1; - - const u32 handle = next_handle++; - - handles.insert_or_assign(handle, std::move(object)); - - return handle; -} - NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { IocCreateParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size); - - if (!params.size) { - LOG_ERROR(Service_NVDRV, "Size is 0"); - return NvResult::BadValue; + LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); + + std::shared_ptr<NvCore::NvMap::Handle> handle_description{}; + auto result = + file.CreateHandle(Common::AlignUp(params.size, YUZU_PAGESIZE), handle_description); + if (result != NvResult::Success) { + LOG_CRITICAL(Service_NVDRV, "Failed to create Object"); + return result; } - - params.handle = CreateObject(params.size); + handle_description->orig_size = params.size; // Orig size is the unaligned size + params.handle = handle_description->id; + LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size); std::memcpy(output.data(), ¶ms, sizeof(params)); return NvResult::Success; @@ -104,63 +92,68 @@ NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { IocAllocParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr); + LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); if (!params.handle) { - LOG_ERROR(Service_NVDRV, "Handle is 0"); + LOG_CRITICAL(Service_NVDRV, "Handle is 0"); return NvResult::BadValue; } if ((params.align - 1) & params.align) { - LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align); + LOG_CRITICAL(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align); return NvResult::BadValue; } - const u32 min_alignment = 0x1000; - if (params.align < min_alignment) { - params.align = min_alignment; + // Force page size alignment at a minimum + if (params.align < YUZU_PAGESIZE) { + params.align = YUZU_PAGESIZE; } - auto object = GetObject(params.handle); - if (!object) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); + auto handle_description{file.GetHandle(params.handle)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); return NvResult::BadValue; } - if (object->status == Object::Status::Allocated) { - LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle); + if (handle_description->allocated) { + LOG_CRITICAL(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle); return NvResult::InsufficientMemory; } - object->flags = params.flags; - object->align = params.align; - object->kind = params.kind; - object->addr = params.addr; - object->status = Object::Status::Allocated; - + const auto result = + handle_description->Alloc(params.flags, params.align, params.kind, params.address); + if (result != NvResult::Success) { + LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle); + return result; + } + ASSERT(system.CurrentProcess() + ->PageTable() + .LockForDeviceAddressSpace(handle_description->address, handle_description->size) + .IsSuccess()); std::memcpy(output.data(), ¶ms, sizeof(params)); - return NvResult::Success; + return result; } NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { IocGetIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "called"); + LOG_DEBUG(Service_NVDRV, "called"); + // See the comment in FromId for extra info on this function if (!params.handle) { - LOG_ERROR(Service_NVDRV, "Handle is zero"); + LOG_CRITICAL(Service_NVDRV, "Error!"); return NvResult::BadValue; } - auto object = GetObject(params.handle); - if (!object) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); - return NvResult::BadValue; + auto handle_description{file.GetHandle(params.handle)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Error!"); + return NvResult::AccessDenied; // This will always return EPERM irrespective of if the + // handle exists or not } - params.id = object->id; - + params.id = handle_description->id; std::memcpy(output.data(), ¶ms, sizeof(params)); return NvResult::Success; } @@ -169,26 +162,29 @@ NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) IocFromIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id); - auto itr = std::find_if(handles.begin(), handles.end(), - [&](const auto& entry) { return entry.second->id == params.id; }); - if (itr == handles.end()) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); + // Handles and IDs are always the same value in nvmap however IDs can be used globally given the + // right permissions. + // Since we don't plan on ever supporting multiprocess we can skip implementing handle refs and + // so this function just does simple validation and passes through the handle id. + if (!params.id) { + LOG_CRITICAL(Service_NVDRV, "Zero Id is invalid!"); return NvResult::BadValue; } - auto& object = itr->second; - if (object->status != Object::Status::Allocated) { - LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); + auto handle_description{file.GetHandle(params.id)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Unregistered handle!"); return NvResult::BadValue; } - itr->second->refcount++; - - // Return the existing handle instead of creating a new one. - params.handle = itr->first; - + auto result = handle_description->Duplicate(false); + if (result != NvResult::Success) { + LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!"); + return result; + } + params.handle = handle_description->id; std::memcpy(output.data(), ¶ms, sizeof(params)); return NvResult::Success; } @@ -199,35 +195,43 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) IocParamParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param); + LOG_DEBUG(Service_NVDRV, "called type={}", params.param); - auto object = GetObject(params.handle); - if (!object) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); + if (!params.handle) { + LOG_CRITICAL(Service_NVDRV, "Invalid handle!"); return NvResult::BadValue; } - if (object->status != Object::Status::Allocated) { - LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); + auto handle_description{file.GetHandle(params.handle)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Not registered handle!"); return NvResult::BadValue; } - switch (static_cast<ParamTypes>(params.param)) { - case ParamTypes::Size: - params.result = object->size; + switch (params.param) { + case HandleParameterType::Size: + params.result = static_cast<u32_le>(handle_description->orig_size); + break; + case HandleParameterType::Alignment: + params.result = static_cast<u32_le>(handle_description->align); break; - case ParamTypes::Alignment: - params.result = object->align; + case HandleParameterType::Base: + params.result = static_cast<u32_le>(-22); // posix EINVAL break; - case ParamTypes::Heap: - // TODO(Subv): Seems to be a hardcoded value? - params.result = 0x40000000; + case HandleParameterType::Heap: + if (handle_description->allocated) + params.result = 0x40000000; + else + params.result = 0; break; - case ParamTypes::Kind: - params.result = object->kind; + case HandleParameterType::Kind: + params.result = handle_description->kind; + break; + case HandleParameterType::IsSharedMemMapped: + params.result = handle_description->is_shared_mem_mapped; break; default: - UNIMPLEMENTED(); + return NvResult::BadValue; } std::memcpy(output.data(), ¶ms, sizeof(params)); @@ -235,46 +239,29 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) } NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { - // TODO(Subv): These flags are unconfirmed. - enum FreeFlags { - Freed = 0, - NotFreedYet = 1, - }; - IocFreeParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_DEBUG(Service_NVDRV, "called"); - auto itr = handles.find(params.handle); - if (itr == handles.end()) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); - return NvResult::BadValue; - } - if (!itr->second->refcount) { - LOG_ERROR( - Service_NVDRV, - "There is no references to this object. The object is already freed. handle={:08X}", - params.handle); - return NvResult::BadValue; + if (!params.handle) { + LOG_CRITICAL(Service_NVDRV, "Handle null freed?"); + return NvResult::Success; } - itr->second->refcount--; - - params.size = itr->second->size; - - if (itr->second->refcount == 0) { - params.flags = Freed; - // The address of the nvmap is written to the output if we're finally freeing it, otherwise - // 0 is written. - params.address = itr->second->addr; + if (auto freeInfo{file.FreeHandle(params.handle, false)}) { + ASSERT(system.CurrentProcess() + ->PageTable() + .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size) + .IsSuccess()); + params.address = freeInfo->address; + params.size = static_cast<u32>(freeInfo->size); + params.flags.raw = 0; + params.flags.map_uncached.Assign(freeInfo->was_uncached); } else { - params.flags = NotFreedYet; - params.address = 0; + // This is possible when there's internel dups or other duplicates. } - handles.erase(params.handle); - std::memcpy(output.data(), ¶ms, sizeof(params)); return NvResult::Success; } diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index d90b69e5a..e9bfd0358 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,15 +9,23 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" +namespace Service::Nvidia::NvCore { +class Container; +} // namespace Service::Nvidia::NvCore + namespace Service::Nvidia::Devices { class nvmap final : public nvdevice { public: - explicit nvmap(Core::System& system_); + explicit nvmap(Core::System& system_, NvCore::Container& container); ~nvmap() override; + nvmap(const nvmap&) = delete; + nvmap& operator=(const nvmap&) = delete; + NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -29,31 +36,15 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; - /// Returns the allocated address of an nvmap object given its handle. - VAddr GetObjectAddress(u32 handle) const; - - /// Represents an nvmap object. - struct Object { - enum class Status { Created, Allocated }; - u32 id; - u32 size; - u32 flags; - u32 align; - u8 kind; - VAddr addr; - Status status; - u32 refcount; - u32 dma_map_addr; + enum class HandleParameterType : u32_le { + Size = 1, + Alignment = 2, + Base = 3, + Heap = 4, + Kind = 5, + IsSharedMemMapped = 6 }; - 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 = 0; @@ -61,9 +52,6 @@ private: /// Id to use for the next object that is created. u32 next_id = 0; - /// Mapping of currently allocated handles to the objects they represent. - std::unordered_map<u32, std::shared_ptr<Object>> handles; - struct IocCreateParams { // Input u32_le size{}; @@ -84,11 +72,11 @@ private: // Input u32_le handle{}; u32_le heap_mask{}; - u32_le flags{}; + NvCore::NvMap::Handle::Flags flags{}; u32_le align{}; u8 kind{}; INSERT_PADDING_BYTES(7); - u64_le addr{}; + u64_le address{}; }; static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size"); @@ -97,14 +85,14 @@ private: INSERT_PADDING_BYTES(4); u64_le address{}; u32_le size{}; - u32_le flags{}; + NvCore::NvMap::Handle::Flags flags{}; }; static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size"); struct IocParamParams { // Input u32_le handle{}; - u32_le param{}; + HandleParameterType param{}; // Output u32_le result{}; }; @@ -118,14 +106,15 @@ private: }; static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); - u32 CreateObject(u32 size); - NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output); NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output); NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output); NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output); NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output); NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output); + + NvCore::Container& container; + NvCore::NvMap& file; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h index 5ab221fc1..0e2f47075 100644 --- a/src/core/hle/service/nvdrv/nvdata.h +++ b/src/core/hle/service/nvdrv/nvdata.h @@ -1,6 +1,6 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once @@ -16,17 +16,11 @@ using DeviceFD = s32; constexpr DeviceFD INVALID_NVDRV_FD = -1; -struct Fence { +struct NvFence { s32 id; u32 value; }; - -static_assert(sizeof(Fence) == 8, "Fence has wrong size"); - -struct MultiFence { - u32 num_fences; - std::array<Fence, 4> fences; -}; +static_assert(sizeof(NvFence) == 8, "NvFence has wrong size"); enum class NvResult : u32 { Success = 0x0, @@ -85,11 +79,15 @@ enum class NvResult : u32 { ModuleNotPresent = 0xA000E, }; +// obtained from +// https://github.com/skyline-emu/skyline/blob/nvdec-dev/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/ctrl.h#L47 enum class EventState { - Free = 0, - Registered = 1, - Waiting = 2, - Busy = 3, + Available = 0, + Waiting = 1, + Cancelling = 2, + Signalling = 3, + Signalled = 4, + Cancelled = 5, }; union Ioctl { diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index aa7e47cbf..5e7b7468f 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -1,6 +1,6 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include <utility> @@ -9,6 +9,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_writable_event.h" +#include "core/hle/service/nvdrv/core/container.h" #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" @@ -16,17 +17,31 @@ #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/nvhost_nvdec.h" +#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" #include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h" #include "core/hle/service/nvdrv/devices/nvhost_vic.h" #include "core/hle/service/nvdrv/devices/nvmap.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/nvdrv_interface.h" #include "core/hle/service/nvdrv/nvmemp.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" #include "core/hle/service/nvflinger/nvflinger.h" +#include "video_core/gpu.h" namespace Service::Nvidia { +EventInterface::EventInterface(Module& module_) : module{module_}, guard{}, on_signal{} {} + +EventInterface::~EventInterface() = default; + +Kernel::KEvent* EventInterface::CreateEvent(std::string name) { + Kernel::KEvent* new_event = module.service_context.CreateEvent(std::move(name)); + return new_event; +} + +void EventInterface::FreeEvent(Kernel::KEvent* event) { + module.service_context.CloseEvent(event); +} + void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, Core::System& system) { auto module_ = std::make_shared<Module>(system); @@ -39,34 +54,54 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger } Module::Module(Core::System& system) - : syncpoint_manager{system.GPU()}, service_context{system, "nvdrv"} { - for (u32 i = 0; i < MaxNvEvents; i++) { - events_interface.events[i].event = - service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", i)); - events_interface.status[i] = EventState::Free; - events_interface.registered[i] = false; - } - auto nvmap_dev = std::make_shared<Devices::nvmap>(system); - devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev); - devices["/dev/nvhost-gpu"] = - std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev, syncpoint_manager); - devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system); - devices["/dev/nvmap"] = nvmap_dev; - devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev); - devices["/dev/nvhost-ctrl"] = - std::make_shared<Devices::nvhost_ctrl>(system, events_interface, syncpoint_manager); - devices["/dev/nvhost-nvdec"] = - std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev, syncpoint_manager); - devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system); - devices["/dev/nvhost-vic"] = - std::make_shared<Devices::nvhost_vic>(system, nvmap_dev, syncpoint_manager); + : service_context{system, "nvdrv"}, events_interface{*this}, container{system.Host1x()} { + builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = + std::make_shared<Devices::nvhost_as_gpu>(system, *this, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-gpu"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = + std::make_shared<Devices::nvhost_gpu>(system, events_interface, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-ctrl-gpu"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = + std::make_shared<Devices::nvhost_ctrl_gpu>(system, events_interface); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvmap"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = + std::make_shared<Devices::nvmap>(system, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvdisp_disp0"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = + std::make_shared<Devices::nvdisp_disp0>(system, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-ctrl"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = + std::make_shared<Devices::nvhost_ctrl>(system, events_interface, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-nvdec"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = + std::make_shared<Devices::nvhost_nvdec>(system, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-nvjpg"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = std::make_shared<Devices::nvhost_nvjpg>(system); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-vic"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = + std::make_shared<Devices::nvhost_vic>(system, container); + return open_files.emplace(fd, device).first; + }; } -Module::~Module() { - for (u32 i = 0; i < MaxNvEvents; i++) { - service_context.CloseEvent(events_interface.events[i].event); - } -} +Module::~Module() {} NvResult Module::VerifyFD(DeviceFD fd) const { if (fd < 0) { @@ -83,18 +118,18 @@ NvResult Module::VerifyFD(DeviceFD fd) const { } DeviceFD Module::Open(const std::string& device_name) { - if (devices.find(device_name) == devices.end()) { + auto it = builders.find(device_name); + if (it == builders.end()) { LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name); return INVALID_NVDRV_FD; } - auto device = devices[device_name]; const DeviceFD fd = next_fd++; + auto& builder = it->second; + auto device = builder(fd)->second; device->OnOpen(fd); - open_files[fd] = std::move(device); - return fd; } @@ -169,22 +204,24 @@ NvResult Module::Close(DeviceFD fd) { return NvResult::Success; } -void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { - for (u32 i = 0; i < MaxNvEvents; i++) { - if (events_interface.assigned_syncpt[i] == syncpoint_id && - events_interface.assigned_value[i] == value) { - events_interface.LiberateEvent(i); - events_interface.events[i].event->GetWritableEvent().Signal(); - } +NvResult Module::QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event) { + if (fd < 0) { + LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); + return NvResult::InvalidState; } -} -Kernel::KReadableEvent& Module::GetEvent(const u32 event_id) { - return events_interface.events[event_id].event->GetReadableEvent(); -} + const auto itr = open_files.find(fd); -Kernel::KWritableEvent& Module::GetEventWriteable(const u32 event_id) { - return events_interface.events[event_id].event->GetWritableEvent(); + if (itr == open_files.end()) { + LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); + return NvResult::NotImplemented; + } + + event = itr->second->QueryEvent(event_id); + if (!event) { + return NvResult::BadParameter; + } + return NvResult::Success; } } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index a5af5b785..146d046a9 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -1,17 +1,21 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once +#include <functional> +#include <list> #include <memory> +#include <string> #include <unordered_map> #include <vector> #include "common/common_types.h" #include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/nvdata.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" +#include "core/hle/service/nvflinger/ui/fence.h" #include "core/hle/service/service.h" namespace Core { @@ -28,81 +32,31 @@ class NVFlinger; namespace Service::Nvidia { +namespace NvCore { +class Container; class SyncpointManager; +} // namespace NvCore namespace Devices { class nvdevice; -} +class nvhost_ctrl; +} // namespace Devices -/// Represents an Nvidia event -struct NvEvent { - Kernel::KEvent* event{}; - Fence fence{}; -}; +class Module; -struct EventInterface { - // Mask representing currently busy events - u64 events_mask{}; - // Each kernel event associated to an NV event - std::array<NvEvent, MaxNvEvents> events; - // The status of the current NVEvent - std::array<EventState, MaxNvEvents> status{}; - // Tells if an NVEvent is registered or not - std::array<bool, MaxNvEvents> registered{}; - // Tells the NVEvent that it has failed. - std::array<bool, MaxNvEvents> failed{}; - // When an NVEvent is waiting on GPU interrupt, this is the sync_point - // associated with it. - std::array<u32, MaxNvEvents> assigned_syncpt{}; - // This is the value of the GPU interrupt for which the NVEvent is waiting - // for. - std::array<u32, MaxNvEvents> assigned_value{}; - // Constant to denote an unasigned syncpoint. - static constexpr u32 unassigned_syncpt = 0xFFFFFFFF; - std::optional<u32> GetFreeEvent() const { - u64 mask = events_mask; - for (u32 i = 0; i < MaxNvEvents; i++) { - const bool is_free = (mask & 0x1) == 0; - if (is_free) { - if (status[i] == EventState::Registered || status[i] == EventState::Free) { - return {i}; - } - } - mask = mask >> 1; - } - return std::nullopt; - } - void SetEventStatus(const u32 event_id, EventState new_status) { - EventState old_status = status[event_id]; - if (old_status == new_status) { - return; - } - status[event_id] = new_status; - if (new_status == EventState::Registered) { - registered[event_id] = true; - } - if (new_status == EventState::Waiting || new_status == EventState::Busy) { - events_mask |= (1ULL << event_id); - } - } - void RegisterEvent(const u32 event_id) { - registered[event_id] = true; - if (status[event_id] == EventState::Free) { - status[event_id] = EventState::Registered; - } - } - void UnregisterEvent(const u32 event_id) { - registered[event_id] = false; - if (status[event_id] == EventState::Registered) { - status[event_id] = EventState::Free; - } - } - void LiberateEvent(const u32 event_id) { - status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free; - events_mask &= ~(1ULL << event_id); - assigned_syncpt[event_id] = unassigned_syncpt; - assigned_value[event_id] = 0; - } +class EventInterface { +public: + explicit EventInterface(Module& module_); + ~EventInterface(); + + Kernel::KEvent* CreateEvent(std::string name); + + void FreeEvent(Kernel::KEvent* event); + +private: + Module& module; + std::mutex guard; + std::list<Devices::nvhost_ctrl*> on_signal; }; class Module final { @@ -112,9 +66,9 @@ public: /// Returns a pointer to one of the available devices, identified by its name. template <typename T> - std::shared_ptr<T> GetDevice(const std::string& name) { - auto itr = devices.find(name); - if (itr == devices.end()) + std::shared_ptr<T> GetDevice(DeviceFD fd) { + auto itr = open_files.find(fd); + if (itr == open_files.end()) return nullptr; return std::static_pointer_cast<T>(itr->second); } @@ -137,28 +91,27 @@ public: /// Closes a device file descriptor and returns operation success. NvResult Close(DeviceFD fd); - void SignalSyncpt(const u32 syncpoint_id, const u32 value); - - Kernel::KReadableEvent& GetEvent(u32 event_id); - - Kernel::KWritableEvent& GetEventWriteable(u32 event_id); + NvResult QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event); private: - /// Manages syncpoints on the host - SyncpointManager syncpoint_manager; + friend class EventInterface; + friend class Service::NVFlinger::NVFlinger; /// Id to use for the next open file descriptor. DeviceFD next_fd = 1; + using FilesContainerType = std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>>; /// Mapping of file descriptors to the devices they reference. - std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>> open_files; + FilesContainerType open_files; - /// Mapping of device node names to their implementation. - std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices; + KernelHelpers::ServiceContext service_context; EventInterface events_interface; - KernelHelpers::ServiceContext service_context; + /// Manages syncpoints on the host + NvCore::Container container; + + std::unordered_map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders; }; /// Registers all NVDRV services with the specified service manager. diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index c16babe14..edbdfee43 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -1,11 +1,12 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include <cinttypes> #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdrv.h" @@ -13,10 +14,6 @@ namespace Service::Nvidia { -void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { - nvdrv->SignalSyncpt(syncpoint_id, value); -} - void NVDRV::Open(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NVDRV, "called"); IPC::ResponseBuilder rb{ctx, 4}; @@ -26,7 +23,7 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) { rb.Push<DeviceFD>(0); rb.PushEnum(NvResult::NotInitialized); - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); return; } @@ -61,7 +58,7 @@ void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) { if (!is_initialized) { ServiceError(ctx, NvResult::NotInitialized); - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); return; } @@ -87,7 +84,7 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) { if (!is_initialized) { ServiceError(ctx, NvResult::NotInitialized); - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); return; } @@ -114,7 +111,7 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) { if (!is_initialized) { ServiceError(ctx, NvResult::NotInitialized); - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); return; } @@ -139,7 +136,7 @@ void NVDRV::Close(Kernel::HLERequestContext& ctx) { if (!is_initialized) { ServiceError(ctx, NvResult::NotInitialized); - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); return; } @@ -165,33 +162,28 @@ void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto fd = rp.Pop<DeviceFD>(); - const auto event_id = rp.Pop<u32>() & 0x00FF; - LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id); + const auto event_id = rp.Pop<u32>(); if (!is_initialized) { ServiceError(ctx, NvResult::NotInitialized); - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); return; } - const auto nv_result = nvdrv->VerifyFD(fd); - if (nv_result != NvResult::Success) { - LOG_ERROR(Service_NVDRV, "Invalid FD specified DeviceFD={}!", fd); - ServiceError(ctx, nv_result); - return; - } + Kernel::KEvent* event = nullptr; + NvResult result = nvdrv->QueryEvent(fd, event_id, event); - if (event_id < MaxNvEvents) { + if (result == NvResult::Success) { IPC::ResponseBuilder rb{ctx, 3, 1}; rb.Push(ResultSuccess); - auto& event = nvdrv->GetEvent(event_id); - event.Clear(); - rb.PushCopyObjects(event); + auto& readable_event = event->GetReadableEvent(); + rb.PushCopyObjects(readable_event); rb.PushEnum(NvResult::Success); } else { + LOG_ERROR(Service_NVDRV, "Invalid event request!"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushEnum(NvResult::BadParameter); + rb.PushEnum(result); } } @@ -230,7 +222,7 @@ void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) { } NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name) - : ServiceFramework{system_, name}, nvdrv{std::move(nvdrv_)} { + : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, nvdrv{std::move(nvdrv_)} { static const FunctionInfo functions[] = { {0, &NVDRV::Open, "Open"}, {1, &NVDRV::Ioctl1, "Ioctl"}, diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h index 0e764c53f..cd58a4f35 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.h +++ b/src/core/hle/service/nvdrv/nvdrv_interface.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -19,8 +18,6 @@ public: explicit NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name); ~NVDRV() override; - void SignalGPUInterruptSyncpt(u32 syncpoint_id, u32 value); - private: void Open(Kernel::HLERequestContext& ctx); void Ioctl1(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/nvdrv/nvmemp.cpp b/src/core/hle/service/nvdrv/nvmemp.cpp index 331c02243..e433580b1 100644 --- a/src/core/hle/service/nvdrv/nvmemp.cpp +++ b/src/core/hle/service/nvdrv/nvmemp.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/logging/log.h" diff --git a/src/core/hle/service/nvdrv/nvmemp.h b/src/core/hle/service/nvdrv/nvmemp.h index 724c27ef9..3d4276327 100644 --- a/src/core/hle/service/nvdrv/nvmemp.h +++ b/src/core/hle/service/nvdrv/nvmemp.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/syncpoint_manager.cpp deleted file mode 100644 index 3b6f55526..000000000 --- a/src/core/hle/service/nvdrv/syncpoint_manager.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/assert.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" -#include "video_core/gpu.h" - -namespace Service::Nvidia { - -SyncpointManager::SyncpointManager(Tegra::GPU& gpu_) : gpu{gpu_} {} - -SyncpointManager::~SyncpointManager() = default; - -u32 SyncpointManager::RefreshSyncpoint(u32 syncpoint_id) { - syncpoints[syncpoint_id].min = gpu.GetSyncpointValue(syncpoint_id); - return GetSyncpointMin(syncpoint_id); -} - -u32 SyncpointManager::AllocateSyncpoint() { - for (u32 syncpoint_id = 1; syncpoint_id < MaxSyncPoints; syncpoint_id++) { - if (!syncpoints[syncpoint_id].is_allocated) { - syncpoints[syncpoint_id].is_allocated = true; - return syncpoint_id; - } - } - UNREACHABLE_MSG("No more available syncpoints!"); - return {}; -} - -u32 SyncpointManager::IncreaseSyncpoint(u32 syncpoint_id, u32 value) { - for (u32 index = 0; index < value; ++index) { - syncpoints[syncpoint_id].max.fetch_add(1, std::memory_order_relaxed); - } - - return GetSyncpointMax(syncpoint_id); -} - -} // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.h b/src/core/hle/service/nvdrv/syncpoint_manager.h deleted file mode 100644 index 99f286474..000000000 --- a/src/core/hle/service/nvdrv/syncpoint_manager.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <array> -#include <atomic> - -#include "common/common_types.h" -#include "core/hle/service/nvdrv/nvdata.h" - -namespace Tegra { -class GPU; -} - -namespace Service::Nvidia { - -class SyncpointManager final { -public: - explicit SyncpointManager(Tegra::GPU& gpu_); - ~SyncpointManager(); - - /** - * Returns true if the specified syncpoint is expired for the given value. - * @param syncpoint_id Syncpoint ID to check. - * @param value Value to check against the specified syncpoint. - * @returns True if the specified syncpoint is expired for the given value, otherwise False. - */ - bool IsSyncpointExpired(u32 syncpoint_id, u32 value) const { - return (GetSyncpointMax(syncpoint_id) - value) >= (GetSyncpointMin(syncpoint_id) - value); - } - - /** - * Gets the lower bound for the specified syncpoint. - * @param syncpoint_id Syncpoint ID to get the lower bound for. - * @returns The lower bound for the specified syncpoint. - */ - u32 GetSyncpointMin(u32 syncpoint_id) const { - return syncpoints.at(syncpoint_id).min.load(std::memory_order_relaxed); - } - - /** - * Gets the uper bound for the specified syncpoint. - * @param syncpoint_id Syncpoint ID to get the upper bound for. - * @returns The upper bound for the specified syncpoint. - */ - u32 GetSyncpointMax(u32 syncpoint_id) const { - return syncpoints.at(syncpoint_id).max.load(std::memory_order_relaxed); - } - - /** - * Refreshes the minimum value for the specified syncpoint. - * @param syncpoint_id Syncpoint ID to be refreshed. - * @returns The new syncpoint minimum value. - */ - u32 RefreshSyncpoint(u32 syncpoint_id); - - /** - * Allocates a new syncoint. - * @returns The syncpoint ID for the newly allocated syncpoint. - */ - u32 AllocateSyncpoint(); - - /** - * Increases the maximum value for the specified syncpoint. - * @param syncpoint_id Syncpoint ID to be increased. - * @param value Value to increase the specified syncpoint by. - * @returns The new syncpoint maximum value. - */ - u32 IncreaseSyncpoint(u32 syncpoint_id, u32 value); - -private: - struct Syncpoint { - std::atomic<u32> min; - std::atomic<u32> max; - std::atomic<bool> is_allocated; - }; - - std::array<Syncpoint, MaxSyncPoints> syncpoints{}; - - Tegra::GPU& gpu; -}; - -} // namespace Service::Nvidia diff --git a/src/core/hle/service/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h new file mode 100644 index 000000000..157333ff8 --- /dev/null +++ b/src/core/hle/service/nvflinger/binder.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/binder/IBinder.h + +#pragma once + +#include "common/common_types.h" + +namespace Kernel { +class HLERequestContext; +class KReadableEvent; +} // namespace Kernel + +namespace Service::android { + +enum class TransactionId { + RequestBuffer = 1, + SetBufferCount = 2, + DequeueBuffer = 3, + DetachBuffer = 4, + DetachNextBuffer = 5, + AttachBuffer = 6, + QueueBuffer = 7, + CancelBuffer = 8, + Query = 9, + Connect = 10, + Disconnect = 11, + AllocateBuffers = 13, + SetPreallocatedBuffer = 14, + GetBufferHistory = 17, +}; + +class IBinder { +public: + virtual ~IBinder() = default; + virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, + u32 flags) = 0; + virtual Kernel::KReadableEvent& GetNativeHandle() = 0; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_item.h b/src/core/hle/service/nvflinger/buffer_item.h new file mode 100644 index 000000000..f73dec4f1 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_item.h @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItem.h + +#pragma once + +#include <memory> + +#include "common/common_types.h" +#include "common/math_util.h" +#include "core/hle/service/nvflinger/ui/fence.h" +#include "core/hle/service/nvflinger/window.h" + +namespace Service::android { + +class GraphicBuffer; + +class BufferItem final { +public: + constexpr BufferItem() = default; + + std::shared_ptr<GraphicBuffer> graphic_buffer; + Fence fence; + Common::Rectangle<s32> crop; + NativeWindowTransform transform{}; + u32 scaling_mode{}; + s64 timestamp{}; + bool is_auto_timestamp{}; + u64 frame_number{}; + + // The default value for buf, used to indicate this doesn't correspond to a slot. + static constexpr s32 INVALID_BUFFER_SLOT = -1; + union { + s32 slot{INVALID_BUFFER_SLOT}; + s32 buf; + }; + + bool is_droppable{}; + bool acquire_called{}; + bool transform_to_display_inverse{}; + s32 swap_interval{}; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp new file mode 100644 index 000000000..6d2c92a2c --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferItemConsumer.cpp + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/service/nvflinger/buffer_item.h" +#include "core/hle/service/nvflinger/buffer_item_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_consumer.h" + +namespace Service::android { + +BufferItemConsumer::BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer_) + : ConsumerBase{std::move(consumer_)} {} + +Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when, + bool wait_for_fence) { + if (!item) { + return Status::BadValue; + } + + std::scoped_lock lock{mutex}; + + if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) { + if (status != Status::NoBufferAvailable) { + LOG_ERROR(Service_NVFlinger, "Failed to acquire buffer: {}", status); + } + return status; + } + + if (wait_for_fence) { + UNIMPLEMENTED(); + } + + item->graphic_buffer = slots[item->slot].graphic_buffer; + + return Status::NoError; +} + +Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) { + std::scoped_lock lock{mutex}; + + if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence); + status != Status::NoError) { + LOG_ERROR(Service_NVFlinger, "Failed to add fence: {}", status); + } + + if (const auto status = ReleaseBufferLocked(item.buf, item.graphic_buffer); + status != Status::NoError) { + LOG_WARNING(Service_NVFlinger, "Failed to release buffer: {}", status); + return status; + } + + return Status::NoError; +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.h b/src/core/hle/service/nvflinger/buffer_item_consumer.h new file mode 100644 index 000000000..69046233d --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_item_consumer.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItemConsumer.h + +#pragma once + +#include <chrono> +#include <memory> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/consumer_base.h" +#include "core/hle/service/nvflinger/status.h" + +namespace Service::android { + +class BufferItem; + +class BufferItemConsumer final : public ConsumerBase { +public: + explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer); + Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when, + bool wait_for_fence = true); + Status ReleaseBuffer(const BufferItem& item, Fence& release_fence); +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp deleted file mode 100644 index 5fead6d1b..000000000 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> - -#include "common/assert.h" -#include "common/logging/log.h" -#include "core/core.h" -#include "core/hle/kernel/k_writable_event.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/nvflinger/buffer_queue.h" - -namespace Service::NVFlinger { - -BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_, - KernelHelpers::ServiceContext& service_context_) - : id(id_), layer_id(layer_id_), service_context{service_context_} { - buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); -} - -BufferQueue::~BufferQueue() { - service_context.CloseEvent(buffer_wait_event); -} - -void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { - ASSERT(slot < buffer_slots); - LOG_WARNING(Service, "Adding graphics buffer {}", slot); - - { - std::unique_lock lock{free_buffers_mutex}; - free_buffers.push_back(slot); - } - free_buffers_condition.notify_one(); - - buffers[slot] = { - .slot = slot, - .status = Buffer::Status::Free, - .igbp_buffer = igbp_buffer, - .transform = {}, - .crop_rect = {}, - .swap_interval = 0, - .multi_fence = {}, - }; - - buffer_wait_event->GetWritableEvent().Signal(); -} - -std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width, - u32 height) { - // Wait for first request before trying to dequeue - { - std::unique_lock lock{free_buffers_mutex}; - free_buffers_condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; }); - } - - if (!is_connect) { - // Buffer was disconnected while the thread was blocked, this is most likely due to - // emulation being stopped - return std::nullopt; - } - - std::unique_lock lock{free_buffers_mutex}; - - auto f_itr = free_buffers.begin(); - auto slot = buffers.size(); - - while (f_itr != free_buffers.end()) { - const Buffer& buffer = buffers[*f_itr]; - if (buffer.status == Buffer::Status::Free && buffer.igbp_buffer.width == width && - buffer.igbp_buffer.height == height) { - slot = *f_itr; - free_buffers.erase(f_itr); - break; - } - ++f_itr; - } - if (slot == buffers.size()) { - return std::nullopt; - } - buffers[slot].status = Buffer::Status::Dequeued; - return {{buffers[slot].slot, &buffers[slot].multi_fence}}; -} - -const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const { - ASSERT(slot < buffers.size()); - ASSERT(buffers[slot].status == Buffer::Status::Dequeued); - ASSERT(buffers[slot].slot == slot); - - return buffers[slot].igbp_buffer; -} - -void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform, - const Common::Rectangle<int>& crop_rect, u32 swap_interval, - Service::Nvidia::MultiFence& multi_fence) { - ASSERT(slot < buffers.size()); - ASSERT(buffers[slot].status == Buffer::Status::Dequeued); - ASSERT(buffers[slot].slot == slot); - - buffers[slot].status = Buffer::Status::Queued; - buffers[slot].transform = transform; - buffers[slot].crop_rect = crop_rect; - buffers[slot].swap_interval = swap_interval; - buffers[slot].multi_fence = multi_fence; - std::unique_lock lock{queue_sequence_mutex}; - queue_sequence.push_back(slot); -} - -void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence) { - ASSERT(slot < buffers.size()); - ASSERT(buffers[slot].status != Buffer::Status::Free); - ASSERT(buffers[slot].slot == slot); - - buffers[slot].status = Buffer::Status::Free; - buffers[slot].multi_fence = multi_fence; - buffers[slot].swap_interval = 0; - - { - std::unique_lock lock{free_buffers_mutex}; - free_buffers.push_back(slot); - } - free_buffers_condition.notify_one(); - - buffer_wait_event->GetWritableEvent().Signal(); -} - -std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { - std::unique_lock lock{queue_sequence_mutex}; - std::size_t buffer_slot = buffers.size(); - // Iterate to find a queued buffer matching the requested slot. - while (buffer_slot == buffers.size() && !queue_sequence.empty()) { - const auto slot = static_cast<std::size_t>(queue_sequence.front()); - ASSERT(slot < buffers.size()); - if (buffers[slot].status == Buffer::Status::Queued) { - ASSERT(buffers[slot].slot == slot); - buffer_slot = slot; - } - queue_sequence.pop_front(); - } - if (buffer_slot == buffers.size()) { - return std::nullopt; - } - buffers[buffer_slot].status = Buffer::Status::Acquired; - return {{buffers[buffer_slot]}}; -} - -void BufferQueue::ReleaseBuffer(u32 slot) { - ASSERT(slot < buffers.size()); - ASSERT(buffers[slot].status == Buffer::Status::Acquired); - ASSERT(buffers[slot].slot == slot); - - buffers[slot].status = Buffer::Status::Free; - { - std::unique_lock lock{free_buffers_mutex}; - free_buffers.push_back(slot); - } - free_buffers_condition.notify_one(); - - buffer_wait_event->GetWritableEvent().Signal(); -} - -void BufferQueue::Connect() { - std::unique_lock lock{queue_sequence_mutex}; - queue_sequence.clear(); - is_connect = true; -} - -void BufferQueue::Disconnect() { - buffers.fill({}); - { - std::unique_lock lock{queue_sequence_mutex}; - queue_sequence.clear(); - } - buffer_wait_event->GetWritableEvent().Signal(); - is_connect = false; - free_buffers_condition.notify_one(); -} - -u32 BufferQueue::Query(QueryType type) { - LOG_WARNING(Service, "(STUBBED) called type={}", type); - - switch (type) { - case QueryType::NativeWindowFormat: - return static_cast<u32>(PixelFormat::RGBA8888); - case QueryType::NativeWindowWidth: - case QueryType::NativeWindowHeight: - break; - case QueryType::NativeWindowMinUndequeuedBuffers: - return 0; - case QueryType::NativeWindowConsumerUsageBits: - return 0; - } - UNIMPLEMENTED_MSG("Unimplemented query type={}", type); - return 0; -} - -Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() { - return buffer_wait_event->GetWritableEvent(); -} - -Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() { - return buffer_wait_event->GetReadableEvent(); -} - -} // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h deleted file mode 100644 index f2a579133..000000000 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <condition_variable> -#include <list> -#include <mutex> -#include <optional> - -#include "common/common_funcs.h" -#include "common/math_util.h" -#include "common/swap.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/service/nvdrv/nvdata.h" - -namespace Kernel { -class KernelCore; -class KEvent; -class KReadableEvent; -class KWritableEvent; -} // namespace Kernel - -namespace Service::KernelHelpers { -class ServiceContext; -} // namespace Service::KernelHelpers - -namespace Service::NVFlinger { - -constexpr u32 buffer_slots = 0x40; -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(6); - u32_le external_format; - INSERT_PADDING_WORDS(10); - 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, - /// The minimum number of buffers that must remain un-dequeued after a buffer has been - /// queued - NativeWindowMinUndequeuedBuffers = 3, - /// The consumer gralloc usage bits currently set by the consumer - NativeWindowConsumerUsageBits = 10, - }; - - explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_, - KernelHelpers::ServiceContext& service_context_); - ~BufferQueue(); - - 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 - Rotate180 = 0x03, - /// Rotate source image 270 degrees clockwise - Rotate270 = 0x07, - }; - - enum class PixelFormat : u32 { - RGBA8888 = 1, - RGBX8888 = 2, - RGB888 = 3, - RGB565 = 4, - BGRA8888 = 5, - RGBA5551 = 6, - RRGBA4444 = 7, - }; - - struct Buffer { - enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 }; - - u32 slot; - Status status = Status::Free; - IGBPBuffer igbp_buffer; - BufferTransformFlags transform; - Common::Rectangle<int> crop_rect; - u32 swap_interval; - Service::Nvidia::MultiFence multi_fence; - }; - - void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer); - std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> DequeueBuffer(u32 width, - u32 height); - const IGBPBuffer& RequestBuffer(u32 slot) const; - void QueueBuffer(u32 slot, BufferTransformFlags transform, - const Common::Rectangle<int>& crop_rect, u32 swap_interval, - Service::Nvidia::MultiFence& multi_fence); - void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence); - std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer(); - void ReleaseBuffer(u32 slot); - void Connect(); - void Disconnect(); - u32 Query(QueryType type); - - u32 GetId() const { - return id; - } - - bool IsConnected() const { - return is_connect; - } - - Kernel::KWritableEvent& GetWritableBufferWaitEvent(); - - Kernel::KReadableEvent& GetBufferWaitEvent(); - -private: - BufferQueue(const BufferQueue&) = delete; - - u32 id{}; - u64 layer_id{}; - std::atomic_bool is_connect{}; - - std::list<u32> free_buffers; - std::array<Buffer, buffer_slots> buffers; - std::list<u32> queue_sequence; - Kernel::KEvent* buffer_wait_event{}; - - std::mutex free_buffers_mutex; - std::condition_variable free_buffers_condition; - - std::mutex queue_sequence_mutex; - - KernelHelpers::ServiceContext& service_context; -}; - -} // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp new file mode 100644 index 000000000..1ce67c771 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp @@ -0,0 +1,213 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp + +#include "common/logging/log.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/hle/service/nvflinger/buffer_item.h" +#include "core/hle/service/nvflinger/buffer_queue_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_core.h" +#include "core/hle/service/nvflinger/producer_listener.h" +#include "core/hle/service/nvflinger/ui/graphic_buffer.h" + +namespace Service::android { + +BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, + Service::Nvidia::NvCore::NvMap& nvmap_) + : core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {} + +BufferQueueConsumer::~BufferQueueConsumer() = default; + +Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer, + std::chrono::nanoseconds expected_present) { + std::scoped_lock lock{core->mutex}; + + // Check that the consumer doesn't currently have the maximum number of buffers acquired. + const s32 num_acquired_buffers{ + static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) { + return slot.buffer_state == BufferState::Acquired; + }))}; + + if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) { + LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})", + num_acquired_buffers, core->max_acquired_buffer_count); + return Status::InvalidOperation; + } + + // Check if the queue is empty. + if (core->queue.empty()) { + return Status::NoBufferAvailable; + } + + auto front(core->queue.begin()); + + // If expected_present is specified, we may not want to return a buffer yet. + if (expected_present.count() != 0) { + constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second + + // The expected_present argument indicates when the buffer is expected to be presented + // on-screen. + while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) { + const auto& buffer_item{core->queue[1]}; + + // If entry[1] is timely, drop entry[0] (and repeat). + const auto desired_present = buffer_item.timestamp; + if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC || + desired_present > expected_present.count()) { + // This buffer is set to display in the near future, or desired_present is garbage. + LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present, + expected_present.count()); + break; + } + + LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present, + expected_present.count(), core->queue.size()); + + if (core->StillTracking(*front)) { + // Front buffer is still in mSlots, so mark the slot as free + slots[front->slot].buffer_state = BufferState::Free; + } + + core->queue.erase(front); + front = core->queue.begin(); + } + + // See if the front buffer is ready to be acquired. + const auto desired_present = front->timestamp; + if (desired_present > expected_present.count() && + desired_present < expected_present.count() + MAX_REASONABLE_NSEC) { + LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present, + expected_present.count()); + return Status::PresentLater; + } + + LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present, + expected_present.count()); + } + + const auto slot = front->slot; + *out_buffer = *front; + + LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot); + + // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to + // avoid unnecessarily remapping this buffer on the consumer side. + if (out_buffer->acquire_called) { + out_buffer->graphic_buffer = nullptr; + } + + core->queue.erase(front); + + // We might have freed a slot while dropping old buffers, or the producer may be blocked + // waiting for the number of buffers in the queue to decrease. + core->SignalDequeueCondition(); + + return Status::NoError; +} + +Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence) { + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_NVFlinger, "slot {} out of range", slot); + return Status::BadValue; + } + + std::shared_ptr<IProducerListener> listener; + { + std::scoped_lock lock{core->mutex}; + + // If the frame number has changed because the buffer has been reallocated, we can ignore + // this ReleaseBuffer for the old buffer. + if (frame_number != slots[slot].frame_number) { + return Status::StaleBufferSlot; + } + + // Make sure this buffer hasn't been queued while acquired by the consumer. + auto current(core->queue.begin()); + while (current != core->queue.end()) { + if (current->slot == slot) { + LOG_ERROR(Service_NVFlinger, "buffer slot {} pending release is currently queued", + slot); + return Status::BadValue; + } + ++current; + } + + slots[slot].buffer_state = BufferState::Free; + + nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true); + + listener = core->connected_producer_listener; + + LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot); + + core->SignalDequeueCondition(); + } + + // Call back without lock held + if (listener != nullptr) { + listener->OnBufferReleased(); + } + + return Status::NoError; +} + +Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_listener, + bool controlled_by_app) { + if (consumer_listener == nullptr) { + LOG_ERROR(Service_NVFlinger, "consumer_listener may not be nullptr"); + return Status::BadValue; + } + + LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app); + + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + core->consumer_listener = consumer_listener; + core->consumer_controlled_by_app = controlled_by_app; + + return Status::NoError; +} + +Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) { + if (out_slot_mask == nullptr) { + LOG_ERROR(Service_NVFlinger, "out_slot_mask may not be nullptr"); + return Status::BadValue; + } + + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + u64 mask = 0; + for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + if (!slots[s].acquire_called) { + mask |= (1ULL << s); + } + } + + // Remove from the mask queued buffers for which acquire has been called, since the consumer + // will not receive their buffer addresses and so must retain their cached information + auto current(core->queue.begin()); + while (current != core->queue.end()) { + if (current->acquire_called) { + mask &= ~(1ULL << current->slot); + } + ++current; + } + + LOG_DEBUG(Service_NVFlinger, "returning mask {}", mask); + *out_slot_mask = mask; + return Status::NoError; +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.h b/src/core/hle/service/nvflinger/buffer_queue_consumer.h new file mode 100644 index 000000000..4ec06ca13 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueConsumer.h + +#pragma once + +#include <chrono> +#include <memory> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/buffer_queue_defs.h" +#include "core/hle/service/nvflinger/status.h" + +namespace Service::Nvidia::NvCore { +class NvMap; +} // namespace Service::Nvidia::NvCore + +namespace Service::android { + +class BufferItem; +class BufferQueueCore; +class IConsumerListener; + +class BufferQueueConsumer final { +public: + explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, + Service::Nvidia::NvCore::NvMap& nvmap_); + ~BufferQueueConsumer(); + + Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); + Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); + Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app); + Status GetReleasedBuffers(u64* out_slot_mask); + +private: + std::shared_ptr<BufferQueueCore> core; + BufferQueueDefs::SlotsType& slots; + Service::Nvidia::NvCore::NvMap& nvmap; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp new file mode 100644 index 000000000..ea4a14ea4 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueCore.cpp + +#include "common/assert.h" + +#include "core/hle/service/nvflinger/buffer_queue_core.h" + +namespace Service::android { + +BufferQueueCore::BufferQueueCore() = default; + +BufferQueueCore::~BufferQueueCore() = default; + +void BufferQueueCore::NotifyShutdown() { + std::scoped_lock lock{mutex}; + + is_shutting_down = true; + + SignalDequeueCondition(); +} + +void BufferQueueCore::SignalDequeueCondition() { + dequeue_condition.notify_all(); +} + +bool BufferQueueCore::WaitForDequeueCondition() { + if (is_shutting_down) { + return false; + } + + dequeue_condition.wait(mutex); + + return true; +} + +s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const { + // If DequeueBuffer is allowed to error out, we don't have to add an extra buffer. + if (!use_async_buffer) { + return max_acquired_buffer_count; + } + + if (dequeue_buffer_cannot_block || async) { + return max_acquired_buffer_count + 1; + } + + return max_acquired_buffer_count; +} + +s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const { + return GetMinUndequeuedBufferCountLocked(async) + 1; +} + +s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const { + const auto min_buffer_count = GetMinMaxBufferCountLocked(async); + auto max_buffer_count = std::max(default_max_buffer_count, min_buffer_count); + + if (override_max_buffer_count != 0) { + ASSERT(override_max_buffer_count >= min_buffer_count); + max_buffer_count = override_max_buffer_count; + } + + // Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed + // need to have their slots preserved. + for (s32 slot = max_buffer_count; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { + const auto state = slots[slot].buffer_state; + if (state == BufferState::Queued || state == BufferState::Dequeued) { + max_buffer_count = slot + 1; + } + } + + return max_buffer_count; +} + +s32 BufferQueueCore::GetPreallocatedBufferCountLocked() const { + return static_cast<s32>(std::count_if(slots.begin(), slots.end(), + [](const auto& slot) { return slot.is_preallocated; })); +} + +void BufferQueueCore::FreeBufferLocked(s32 slot) { + LOG_DEBUG(Service_NVFlinger, "slot {}", slot); + + slots[slot].graphic_buffer.reset(); + + slots[slot].buffer_state = BufferState::Free; + slots[slot].frame_number = UINT32_MAX; + slots[slot].acquire_called = false; + slots[slot].fence = Fence::NoFence(); +} + +void BufferQueueCore::FreeAllBuffersLocked() { + buffer_has_been_queued = false; + + for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { + FreeBufferLocked(slot); + } +} + +bool BufferQueueCore::StillTracking(const BufferItem& item) const { + const BufferSlot& slot = slots[item.slot]; + + return (slot.graphic_buffer != nullptr) && (item.graphic_buffer == slot.graphic_buffer); +} + +void BufferQueueCore::WaitWhileAllocatingLocked() const { + while (is_allocating) { + is_allocating_condition.wait(mutex); + } +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h new file mode 100644 index 000000000..ca6baefaf --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_core.h @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueCore.h + +#pragma once + +#include <condition_variable> +#include <list> +#include <memory> +#include <mutex> +#include <set> +#include <vector> + +#include "core/hle/service/nvflinger/buffer_item.h" +#include "core/hle/service/nvflinger/buffer_queue_defs.h" +#include "core/hle/service/nvflinger/pixel_format.h" +#include "core/hle/service/nvflinger/status.h" +#include "core/hle/service/nvflinger/window.h" + +namespace Service::android { + +class IConsumerListener; +class IProducerListener; + +class BufferQueueCore final { + friend class BufferQueueProducer; + friend class BufferQueueConsumer; + +public: + static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT; + + BufferQueueCore(); + ~BufferQueueCore(); + + void NotifyShutdown(); + +private: + void SignalDequeueCondition(); + bool WaitForDequeueCondition(); + + s32 GetMinUndequeuedBufferCountLocked(bool async) const; + s32 GetMinMaxBufferCountLocked(bool async) const; + s32 GetMaxBufferCountLocked(bool async) const; + s32 GetPreallocatedBufferCountLocked() const; + void FreeBufferLocked(s32 slot); + void FreeAllBuffersLocked(); + bool StillTracking(const BufferItem& item) const; + void WaitWhileAllocatingLocked() const; + +private: + mutable std::mutex mutex; + bool is_abandoned{}; + bool consumer_controlled_by_app{}; + std::shared_ptr<IConsumerListener> consumer_listener; + u32 consumer_usage_bit{}; + NativeWindowApi connected_api{NativeWindowApi::NoConnectedApi}; + std::shared_ptr<IProducerListener> connected_producer_listener; + BufferQueueDefs::SlotsType slots{}; + std::vector<BufferItem> queue; + s32 override_max_buffer_count{}; + mutable std::condition_variable_any dequeue_condition; + const bool use_async_buffer{}; // This is always disabled on HOS + bool dequeue_buffer_cannot_block{}; + PixelFormat default_buffer_format{PixelFormat::Rgba8888}; + u32 default_width{1}; + u32 default_height{1}; + s32 default_max_buffer_count{2}; + const s32 max_acquired_buffer_count{}; // This is always zero on HOS + bool buffer_has_been_queued{}; + u64 frame_counter{}; + u32 transform_hint{}; + bool is_allocating{}; + mutable std::condition_variable_any is_allocating_condition; + bool is_shutting_down{}; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_defs.h b/src/core/hle/service/nvflinger/buffer_queue_defs.h new file mode 100644 index 000000000..334445213 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_defs.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueDefs.h + +#pragma once + +#include <array> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/buffer_slot.h" + +namespace Service::android::BufferQueueDefs { + +// BufferQueue will keep track of at most this value of buffers. +constexpr s32 NUM_BUFFER_SLOTS = 64; + +using SlotsType = std::array<BufferSlot, NUM_BUFFER_SLOTS>; + +} // namespace Service::android::BufferQueueDefs diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp new file mode 100644 index 000000000..d4ab23a10 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp @@ -0,0 +1,927 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueProducer.cpp + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/core.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/kernel/k_writable_event.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/hle/service/nvflinger/buffer_queue_core.h" +#include "core/hle/service/nvflinger/buffer_queue_producer.h" +#include "core/hle/service/nvflinger/consumer_listener.h" +#include "core/hle/service/nvflinger/parcel.h" +#include "core/hle/service/nvflinger/ui/graphic_buffer.h" +#include "core/hle/service/nvflinger/window.h" +#include "core/hle/service/vi/vi.h" + +namespace Service::android { + +BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, + std::shared_ptr<BufferQueueCore> buffer_queue_core_, + Service::Nvidia::NvCore::NvMap& nvmap_) + : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots), + nvmap(nvmap_) { + buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); +} + +BufferQueueProducer::~BufferQueueProducer() { + service_context.CloseEvent(buffer_wait_event); +} + +Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) { + LOG_DEBUG(Service_NVFlinger, "slot {}", slot); + + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot, + BufferQueueDefs::NUM_BUFFER_SLOTS); + return Status::BadValue; + } else if (slots[slot].buffer_state != BufferState::Dequeued) { + LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot, + slots[slot].buffer_state); + return Status::BadValue; + } + + slots[slot].request_buffer_called = true; + *buf = slots[slot].graphic_buffer; + + return Status::NoError; +} + +Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { + LOG_DEBUG(Service_NVFlinger, "count = {}", buffer_count); + + std::shared_ptr<IConsumerListener> listener; + { + std::scoped_lock lock{core->mutex}; + core->WaitWhileAllocatingLocked(); + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + if (buffer_count > BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_NVFlinger, "buffer_count {} too large (max {})", buffer_count, + BufferQueueDefs::NUM_BUFFER_SLOTS); + return Status::BadValue; + } + + // There must be no dequeued buffers when changing the buffer count. + for (s32 s{}; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + if (slots[s].buffer_state == BufferState::Dequeued) { + LOG_ERROR(Service_NVFlinger, "buffer owned by producer"); + return Status::BadValue; + } + } + + if (buffer_count == 0) { + core->override_max_buffer_count = 0; + core->SignalDequeueCondition(); + return Status::NoError; + } + + const s32 min_buffer_slots = core->GetMinMaxBufferCountLocked(false); + if (buffer_count < min_buffer_slots) { + LOG_ERROR(Service_NVFlinger, "requested buffer count {} is less than minimum {}", + buffer_count, min_buffer_slots); + return Status::BadValue; + } + + // Here we are guaranteed that the producer doesn't have any dequeued buffers and will + // release all of its buffer references. + if (core->GetPreallocatedBufferCountLocked() <= 0) { + core->FreeAllBuffersLocked(); + } + + core->override_max_buffer_count = buffer_count; + core->SignalDequeueCondition(); + buffer_wait_event->GetWritableEvent().Signal(); + listener = core->consumer_listener; + } + + // Call back without lock held + if (listener != nullptr) { + listener->OnBuffersReleased(); + } + + return Status::NoError; +} + +Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, + Status* return_flags) const { + bool try_again = true; + + while (try_again) { + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + const s32 max_buffer_count = core->GetMaxBufferCountLocked(async); + if (async && core->override_max_buffer_count) { + if (core->override_max_buffer_count < max_buffer_count) { + LOG_ERROR(Service_NVFlinger, "async mode is invalid with buffer count override"); + return Status::BadValue; + } + } + + // Free up any buffers that are in slots beyond the max buffer count + for (s32 s = max_buffer_count; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + ASSERT(slots[s].buffer_state == BufferState::Free); + if (slots[s].graphic_buffer != nullptr) { + core->FreeBufferLocked(s); + *return_flags |= Status::ReleaseAllBuffers; + } + } + + // Look for a free buffer to give to the client + *found = BufferQueueCore::INVALID_BUFFER_SLOT; + s32 dequeued_count{}; + s32 acquired_count{}; + for (s32 s{}; s < max_buffer_count; ++s) { + switch (slots[s].buffer_state) { + case BufferState::Dequeued: + ++dequeued_count; + break; + case BufferState::Acquired: + ++acquired_count; + break; + case BufferState::Free: + // We return the oldest of the free buffers to avoid stalling the producer if + // possible, since the consumer may still have pending reads of in-flight buffers + if (*found == BufferQueueCore::INVALID_BUFFER_SLOT || + slots[s].frame_number < slots[*found].frame_number) { + *found = s; + } + break; + default: + break; + } + } + + // Producers are not allowed to dequeue more than one buffer if they did not set a buffer + // count + if (!core->override_max_buffer_count && dequeued_count) { + LOG_ERROR(Service_NVFlinger, + "can't dequeue multiple buffers without setting the buffer count"); + return Status::InvalidOperation; + } + + // See whether a buffer has been queued since the last SetBufferCount so we know whether to + // perform the min undequeued buffers check below + if (core->buffer_has_been_queued) { + // Make sure the producer is not trying to dequeue more buffers than allowed + const s32 new_undequeued_count = max_buffer_count - (dequeued_count + 1); + const s32 min_undequeued_count = core->GetMinUndequeuedBufferCountLocked(async); + if (new_undequeued_count < min_undequeued_count) { + LOG_ERROR(Service_NVFlinger, + "min undequeued buffer count({}) exceeded (dequeued={} undequeued={})", + min_undequeued_count, dequeued_count, new_undequeued_count); + return Status::InvalidOperation; + } + } + + // If we disconnect and reconnect quickly, we can be in a state where our slots are empty + // but we have many buffers in the queue. This can cause us to run out of memory if we + // outrun the consumer. Wait here if it looks like we have too many buffers queued up. + const bool too_many_buffers = core->queue.size() > static_cast<size_t>(max_buffer_count); + if (too_many_buffers) { + LOG_ERROR(Service_NVFlinger, "queue size is {}, waiting", core->queue.size()); + } + + // If no buffer is found, or if the queue has too many buffers outstanding, wait for a + // buffer to be acquired or released, or for the max buffer count to change. + try_again = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) || too_many_buffers; + if (try_again) { + // Return an error if we're in non-blocking mode (producer and consumer are controlled + // by the application). + if (core->dequeue_buffer_cannot_block && + (acquired_count <= core->max_acquired_buffer_count)) { + return Status::WouldBlock; + } + + if (!core->WaitForDequeueCondition()) { + // We are no longer running + return Status::NoError; + } + } + } + + return Status::NoError; +} + +Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool async, u32 width, + u32 height, PixelFormat format, u32 usage) { + LOG_DEBUG(Service_NVFlinger, "async={} w={} h={} format={}, usage={}", async ? "true" : "false", + width, height, format, usage); + + if ((width != 0 && height == 0) || (width == 0 && height != 0)) { + LOG_ERROR(Service_NVFlinger, "invalid size: w={} h={}", width, height); + return Status::BadValue; + } + + Status return_flags = Status::NoError; + bool attached_by_consumer = false; + { + std::scoped_lock lock{core->mutex}; + core->WaitWhileAllocatingLocked(); + + if (format == PixelFormat::NoFormat) { + format = core->default_buffer_format; + } + + // Enable the usage bits the consumer requested + usage |= core->consumer_usage_bit; + + s32 found{}; + Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags); + if (status != Status::NoError) { + return status; + } + + // This should not happen + if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { + LOG_ERROR(Service_NVFlinger, "no available buffer slots"); + return Status::Busy; + } + + *out_slot = found; + + attached_by_consumer = slots[found].attached_by_consumer; + + const bool use_default_size = !width && !height; + if (use_default_size) { + width = core->default_width; + height = core->default_height; + } + + slots[found].buffer_state = BufferState::Dequeued; + + const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer); + if ((buffer == nullptr) || (buffer->Width() != width) || (buffer->Height() != height) || + (buffer->Format() != format) || ((buffer->Usage() & usage) != usage)) { + slots[found].acquire_called = false; + slots[found].graphic_buffer = nullptr; + slots[found].request_buffer_called = false; + slots[found].fence = Fence::NoFence(); + + return_flags |= Status::BufferNeedsReallocation; + } + + *out_fence = slots[found].fence; + slots[found].fence = Fence::NoFence(); + } + + if ((return_flags & Status::BufferNeedsReallocation) != Status::None) { + LOG_DEBUG(Service_NVFlinger, "allocating a new buffer for slot {}", *out_slot); + + auto graphic_buffer = std::make_shared<GraphicBuffer>(width, height, format, usage); + if (graphic_buffer == nullptr) { + LOG_ERROR(Service_NVFlinger, "creating GraphicBuffer failed"); + return Status::NoMemory; + } + + { + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + slots[*out_slot].frame_number = UINT32_MAX; + slots[*out_slot].graphic_buffer = graphic_buffer; + } + } + + if (attached_by_consumer) { + return_flags |= Status::BufferNeedsReallocation; + } + + LOG_DEBUG(Service_NVFlinger, "returning slot={} frame={}, flags={}", *out_slot, + slots[*out_slot].frame_number, return_flags); + + return return_flags; +} + +Status BufferQueueProducer::DetachBuffer(s32 slot) { + LOG_DEBUG(Service_NVFlinger, "slot {}", slot); + + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_NVFlinger, "slot {} out of range [0, {})", slot, + BufferQueueDefs::NUM_BUFFER_SLOTS); + return Status::BadValue; + } else if (slots[slot].buffer_state != BufferState::Dequeued) { + LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot, + slots[slot].buffer_state); + return Status::BadValue; + } else if (!slots[slot].request_buffer_called) { + LOG_ERROR(Service_NVFlinger, "buffer in slot {} has not been requested", slot); + return Status::BadValue; + } + + core->FreeBufferLocked(slot); + core->SignalDequeueCondition(); + + return Status::NoError; +} + +Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer, + Fence* out_fence) { + if (out_buffer == nullptr) { + LOG_ERROR(Service_NVFlinger, "out_buffer must not be nullptr"); + return Status::BadValue; + } else if (out_fence == nullptr) { + LOG_ERROR(Service_NVFlinger, "out_fence must not be nullptr"); + return Status::BadValue; + } + + std::scoped_lock lock{core->mutex}; + core->WaitWhileAllocatingLocked(); + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + // Find the oldest valid slot + int found = BufferQueueCore::INVALID_BUFFER_SLOT; + for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + if (slots[s].buffer_state == BufferState::Free && slots[s].graphic_buffer != nullptr) { + if (found == BufferQueueCore::INVALID_BUFFER_SLOT || + slots[s].frame_number < slots[found].frame_number) { + found = s; + } + } + } + + if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { + return Status::NoMemory; + } + + LOG_DEBUG(Service_NVFlinger, "Detached slot {}", found); + + *out_buffer = slots[found].graphic_buffer; + *out_fence = slots[found].fence; + + core->FreeBufferLocked(found); + + return Status::NoError; +} + +Status BufferQueueProducer::AttachBuffer(s32* out_slot, + const std::shared_ptr<GraphicBuffer>& buffer) { + if (out_slot == nullptr) { + LOG_ERROR(Service_NVFlinger, "out_slot must not be nullptr"); + return Status::BadValue; + } else if (buffer == nullptr) { + LOG_ERROR(Service_NVFlinger, "Cannot attach nullptr buffer"); + return Status::BadValue; + } + + std::scoped_lock lock{core->mutex}; + core->WaitWhileAllocatingLocked(); + + Status return_flags = Status::NoError; + s32 found{}; + + const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags); + if (status != Status::NoError) { + return status; + } + + // This should not happen + if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { + LOG_ERROR(Service_NVFlinger, "No available buffer slots"); + return Status::Busy; + } + + *out_slot = found; + + LOG_DEBUG(Service_NVFlinger, "Returning slot {} flags={}", *out_slot, return_flags); + + slots[*out_slot].graphic_buffer = buffer; + slots[*out_slot].buffer_state = BufferState::Dequeued; + slots[*out_slot].fence = Fence::NoFence(); + slots[*out_slot].request_buffer_called = true; + + return return_flags; +} + +Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, + QueueBufferOutput* output) { + s64 timestamp{}; + bool is_auto_timestamp{}; + Common::Rectangle<s32> crop; + NativeWindowScalingMode scaling_mode{}; + NativeWindowTransform transform; + u32 sticky_transform_{}; + bool async{}; + s32 swap_interval{}; + Fence fence{}; + + input.Deflate(×tamp, &is_auto_timestamp, &crop, &scaling_mode, &transform, + &sticky_transform_, &async, &swap_interval, &fence); + + switch (scaling_mode) { + case NativeWindowScalingMode::Freeze: + case NativeWindowScalingMode::ScaleToWindow: + case NativeWindowScalingMode::ScaleCrop: + case NativeWindowScalingMode::NoScaleCrop: + break; + default: + LOG_ERROR(Service_NVFlinger, "unknown scaling mode {}", scaling_mode); + return Status::BadValue; + } + + std::shared_ptr<IConsumerListener> frame_available_listener; + std::shared_ptr<IConsumerListener> frame_replaced_listener; + s32 callback_ticket{}; + BufferItem item; + + { + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + const s32 max_buffer_count = core->GetMaxBufferCountLocked(async); + if (async && core->override_max_buffer_count) { + if (core->override_max_buffer_count < max_buffer_count) { + LOG_ERROR(Service_NVFlinger, "async mode is invalid with " + "buffer count override"); + return Status::BadValue; + } + } + + if (slot < 0 || slot >= max_buffer_count) { + LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot, + max_buffer_count); + return Status::BadValue; + } else if (slots[slot].buffer_state != BufferState::Dequeued) { + LOG_ERROR(Service_NVFlinger, + "slot {} is not owned by the producer " + "(state = {})", + slot, slots[slot].buffer_state); + return Status::BadValue; + } else if (!slots[slot].request_buffer_called) { + LOG_ERROR(Service_NVFlinger, + "slot {} was queued without requesting " + "a buffer", + slot); + return Status::BadValue; + } + + LOG_DEBUG(Service_NVFlinger, + "slot={} frame={} time={} crop=[{},{},{},{}] transform={} scale={}", slot, + core->frame_counter + 1, timestamp, crop.Left(), crop.Top(), crop.Right(), + crop.Bottom(), transform, scaling_mode); + + const std::shared_ptr<GraphicBuffer>& graphic_buffer(slots[slot].graphic_buffer); + Common::Rectangle<s32> buffer_rect(graphic_buffer->Width(), graphic_buffer->Height()); + Common::Rectangle<s32> cropped_rect; + [[maybe_unused]] const bool unused = crop.Intersect(buffer_rect, &cropped_rect); + + if (cropped_rect != crop) { + LOG_ERROR(Service_NVFlinger, "crop rect is not contained within the buffer in slot {}", + slot); + return Status::BadValue; + } + + slots[slot].fence = fence; + slots[slot].buffer_state = BufferState::Queued; + ++core->frame_counter; + slots[slot].frame_number = core->frame_counter; + + item.acquire_called = slots[slot].acquire_called; + item.graphic_buffer = slots[slot].graphic_buffer; + item.crop = crop; + item.transform = transform & ~NativeWindowTransform::InverseDisplay; + item.transform_to_display_inverse = + (transform & NativeWindowTransform::InverseDisplay) != NativeWindowTransform::None; + item.scaling_mode = static_cast<u32>(scaling_mode); + item.timestamp = timestamp; + item.is_auto_timestamp = is_auto_timestamp; + item.frame_number = core->frame_counter; + item.slot = slot; + item.fence = fence; + item.is_droppable = core->dequeue_buffer_cannot_block || async; + item.swap_interval = swap_interval; + + nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true); + + sticky_transform = sticky_transform_; + + if (core->queue.empty()) { + // When the queue is empty, we can simply queue this buffer + core->queue.push_back(item); + frame_available_listener = core->consumer_listener; + } else { + // When the queue is not empty, we need to look at the front buffer + // state to see if we need to replace it + auto front(core->queue.begin()); + + if (front->is_droppable) { + // If the front queued buffer is still being tracked, we first + // mark it as freed + if (core->StillTracking(*front)) { + slots[front->slot].buffer_state = BufferState::Free; + // Reset the frame number of the freed buffer so that it is the first in line to + // be dequeued again + slots[front->slot].frame_number = 0; + } + // Overwrite the droppable buffer with the incoming one + *front = item; + frame_replaced_listener = core->consumer_listener; + } else { + core->queue.push_back(item); + frame_available_listener = core->consumer_listener; + } + } + + core->buffer_has_been_queued = true; + core->SignalDequeueCondition(); + output->Inflate(core->default_width, core->default_height, core->transform_hint, + static_cast<u32>(core->queue.size())); + + // Take a ticket for the callback functions + callback_ticket = next_callback_ticket++; + } + + // Don't send the GraphicBuffer through the callback, and don't send the slot number, since the + // consumer shouldn't need it + item.graphic_buffer.reset(); + item.slot = BufferItem::INVALID_BUFFER_SLOT; + + // Call back without the main BufferQueue lock held, but with the callback lock held so we can + // ensure that callbacks occur in order + { + std::scoped_lock lock{callback_mutex}; + while (callback_ticket != current_callback_ticket) { + callback_condition.wait(callback_mutex); + } + + if (frame_available_listener != nullptr) { + frame_available_listener->OnFrameAvailable(item); + } else if (frame_replaced_listener != nullptr) { + frame_replaced_listener->OnFrameReplaced(item); + } + + ++current_callback_ticket; + callback_condition.notify_all(); + } + + return Status::NoError; +} + +void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) { + LOG_DEBUG(Service_NVFlinger, "slot {}", slot); + + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return; + } + + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot, + BufferQueueDefs::NUM_BUFFER_SLOTS); + return; + } else if (slots[slot].buffer_state != BufferState::Dequeued) { + LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot, + slots[slot].buffer_state); + return; + } + + slots[slot].buffer_state = BufferState::Free; + slots[slot].frame_number = 0; + slots[slot].fence = fence; + + core->SignalDequeueCondition(); + buffer_wait_event->GetWritableEvent().Signal(); +} + +Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { + std::scoped_lock lock{core->mutex}; + + if (out_value == nullptr) { + LOG_ERROR(Service_NVFlinger, "outValue was nullptr"); + return Status::BadValue; + } + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + u32 value{}; + switch (what) { + case NativeWindow::Width: + value = core->default_width; + break; + case NativeWindow::Height: + value = core->default_height; + break; + case NativeWindow::Format: + value = static_cast<u32>(core->default_buffer_format); + break; + case NativeWindow::MinUndequeedBuffers: + value = core->GetMinUndequeuedBufferCountLocked(false); + break; + case NativeWindow::StickyTransform: + value = sticky_transform; + break; + case NativeWindow::ConsumerRunningBehind: + value = (core->queue.size() > 1); + break; + case NativeWindow::ConsumerUsageBits: + value = core->consumer_usage_bit; + break; + default: + ASSERT(false); + return Status::BadValue; + } + + LOG_DEBUG(Service_NVFlinger, "what = {}, value = {}", what, value); + + *out_value = static_cast<s32>(value); + + return Status::NoError; +} + +Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener, + NativeWindowApi api, bool producer_controlled_by_app, + QueueBufferOutput* output) { + std::scoped_lock lock{core->mutex}; + + LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api, + producer_controlled_by_app); + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + if (core->consumer_listener == nullptr) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has no consumer"); + return Status::NoInit; + } + + if (output == nullptr) { + LOG_ERROR(Service_NVFlinger, "output was nullptr"); + return Status::BadValue; + } + + if (core->connected_api != NativeWindowApi::NoConnectedApi) { + LOG_ERROR(Service_NVFlinger, "already connected (cur = {} req = {})", core->connected_api, + api); + return Status::BadValue; + } + + Status status = Status::NoError; + switch (api) { + case NativeWindowApi::Egl: + case NativeWindowApi::Cpu: + case NativeWindowApi::Media: + case NativeWindowApi::Camera: + core->connected_api = api; + output->Inflate(core->default_width, core->default_height, core->transform_hint, + static_cast<u32>(core->queue.size())); + core->connected_producer_listener = listener; + break; + default: + LOG_ERROR(Service_NVFlinger, "unknown api = {}", api); + status = Status::BadValue; + break; + } + + core->buffer_has_been_queued = false; + core->dequeue_buffer_cannot_block = + core->consumer_controlled_by_app && producer_controlled_by_app; + + return status; +} + +Status BufferQueueProducer::Disconnect(NativeWindowApi api) { + LOG_DEBUG(Service_NVFlinger, "api = {}", api); + + Status status = Status::NoError; + std::shared_ptr<IConsumerListener> listener; + + { + std::scoped_lock lock{core->mutex}; + + core->WaitWhileAllocatingLocked(); + + if (core->is_abandoned) { + // Disconnecting after the surface has been abandoned is a no-op. + return Status::NoError; + } + + switch (api) { + case NativeWindowApi::Egl: + case NativeWindowApi::Cpu: + case NativeWindowApi::Media: + case NativeWindowApi::Camera: + if (core->connected_api == api) { + core->FreeAllBuffersLocked(); + core->connected_producer_listener = nullptr; + core->connected_api = NativeWindowApi::NoConnectedApi; + core->SignalDequeueCondition(); + buffer_wait_event->GetWritableEvent().Signal(); + listener = core->consumer_listener; + } else { + LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})", + core->connected_api, api); + status = Status::BadValue; + } + break; + default: + LOG_ERROR(Service_NVFlinger, "unknown api = {}", api); + status = Status::BadValue; + break; + } + } + + // Call back without lock held + if (listener != nullptr) { + listener->OnBuffersReleased(); + } + + return status; +} + +Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, + const std::shared_ptr<GraphicBuffer>& buffer) { + LOG_DEBUG(Service_NVFlinger, "slot {}", slot); + + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + return Status::BadValue; + } + + std::scoped_lock lock{core->mutex}; + + slots[slot] = {}; + slots[slot].graphic_buffer = buffer; + slots[slot].frame_number = 0; + + // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for + // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this. + if (buffer) { + slots[slot].is_preallocated = true; + + core->override_max_buffer_count = core->GetPreallocatedBufferCountLocked(); + core->default_width = buffer->Width(); + core->default_height = buffer->Height(); + core->default_buffer_format = buffer->Format(); + } + + core->SignalDequeueCondition(); + buffer_wait_event->GetWritableEvent().Signal(); + + return Status::NoError; +} + +void BufferQueueProducer::Transact(Kernel::HLERequestContext& ctx, TransactionId code, u32 flags) { + Status status{Status::NoError}; + Parcel parcel_in{ctx.ReadBuffer()}; + Parcel parcel_out{}; + + switch (code) { + case TransactionId::Connect: { + const auto enable_listener = parcel_in.Read<bool>(); + const auto api = parcel_in.Read<NativeWindowApi>(); + const auto producer_controlled_by_app = parcel_in.Read<bool>(); + + UNIMPLEMENTED_IF_MSG(enable_listener, "Listener is unimplemented!"); + + std::shared_ptr<IProducerListener> listener; + QueueBufferOutput output{}; + + status = Connect(listener, api, producer_controlled_by_app, &output); + + parcel_out.Write(output); + break; + } + case TransactionId::SetPreallocatedBuffer: { + const auto slot = parcel_in.Read<s32>(); + const auto buffer = parcel_in.ReadObject<GraphicBuffer>(); + + status = SetPreallocatedBuffer(slot, buffer); + break; + } + case TransactionId::DequeueBuffer: { + const auto is_async = parcel_in.Read<bool>(); + const auto width = parcel_in.Read<u32>(); + const auto height = parcel_in.Read<u32>(); + const auto pixel_format = parcel_in.Read<PixelFormat>(); + const auto usage = parcel_in.Read<u32>(); + + s32 slot{}; + Fence fence{}; + + status = DequeueBuffer(&slot, &fence, is_async, width, height, pixel_format, usage); + + parcel_out.Write(slot); + parcel_out.WriteObject(&fence); + break; + } + case TransactionId::RequestBuffer: { + const auto slot = parcel_in.Read<s32>(); + + std::shared_ptr<GraphicBuffer> buf; + + status = RequestBuffer(slot, &buf); + + parcel_out.WriteObject(buf); + break; + } + case TransactionId::QueueBuffer: { + const auto slot = parcel_in.Read<s32>(); + + QueueBufferInput input{parcel_in}; + QueueBufferOutput output; + + status = QueueBuffer(slot, input, &output); + + parcel_out.Write(output); + break; + } + case TransactionId::Query: { + const auto what = parcel_in.Read<NativeWindow>(); + + s32 value{}; + + status = Query(what, &value); + + parcel_out.Write(value); + break; + } + case TransactionId::CancelBuffer: { + const auto slot = parcel_in.Read<s32>(); + const auto fence = parcel_in.ReadFlattened<Fence>(); + + CancelBuffer(slot, fence); + break; + } + case TransactionId::Disconnect: { + const auto api = parcel_in.Read<NativeWindowApi>(); + + status = Disconnect(api); + break; + } + case TransactionId::DetachBuffer: { + const auto slot = parcel_in.Read<s32>(); + + status = DetachBuffer(slot); + break; + } + case TransactionId::SetBufferCount: { + const auto buffer_count = parcel_in.Read<s32>(); + + status = SetBufferCount(buffer_count); + break; + } + case TransactionId::GetBufferHistory: + LOG_WARNING(Service_NVFlinger, "(STUBBED) called, transaction=GetBufferHistory"); + break; + default: + ASSERT_MSG(false, "Unimplemented TransactionId {}", code); + break; + } + + parcel_out.Write(status); + + ctx.WriteBuffer(parcel_out.Serialize()); +} + +Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() { + return buffer_wait_event->GetReadableEvent(); +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h new file mode 100644 index 000000000..0ba03a568 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueProducer.h + +#pragma once + +#include <condition_variable> +#include <memory> +#include <mutex> + +#include "common/common_funcs.h" +#include "core/hle/service/nvdrv/nvdata.h" +#include "core/hle/service/nvflinger/binder.h" +#include "core/hle/service/nvflinger/buffer_queue_defs.h" +#include "core/hle/service/nvflinger/buffer_slot.h" +#include "core/hle/service/nvflinger/graphic_buffer_producer.h" +#include "core/hle/service/nvflinger/pixel_format.h" +#include "core/hle/service/nvflinger/status.h" +#include "core/hle/service/nvflinger/window.h" + +namespace Kernel { +class KernelCore; +class KEvent; +class KReadableEvent; +class KWritableEvent; +} // namespace Kernel + +namespace Service::KernelHelpers { +class ServiceContext; +} // namespace Service::KernelHelpers + +namespace Service::Nvidia::NvCore { +class NvMap; +} // namespace Service::Nvidia::NvCore + +namespace Service::android { + +class BufferQueueCore; +class IProducerListener; + +class BufferQueueProducer final : public IBinder { +public: + explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, + std::shared_ptr<BufferQueueCore> buffer_queue_core_, + Service::Nvidia::NvCore::NvMap& nvmap_); + ~BufferQueueProducer(); + + void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override; + + Kernel::KReadableEvent& GetNativeHandle() override; + +public: + Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf); + Status SetBufferCount(s32 buffer_count); + Status DequeueBuffer(s32* out_slot, android::Fence* out_fence, bool async, u32 width, + u32 height, PixelFormat format, u32 usage); + Status DetachBuffer(s32 slot); + Status DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer, Fence* out_fence); + Status AttachBuffer(s32* outSlot, const std::shared_ptr<GraphicBuffer>& buffer); + Status QueueBuffer(s32 slot, const QueueBufferInput& input, QueueBufferOutput* output); + void CancelBuffer(s32 slot, const Fence& fence); + Status Query(NativeWindow what, s32* out_value); + Status Connect(const std::shared_ptr<IProducerListener>& listener, NativeWindowApi api, + bool producer_controlled_by_app, QueueBufferOutput* output); + + Status Disconnect(NativeWindowApi api); + Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer); + +private: + BufferQueueProducer(const BufferQueueProducer&) = delete; + + Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const; + + Kernel::KEvent* buffer_wait_event{}; + Service::KernelHelpers::ServiceContext& service_context; + + std::shared_ptr<BufferQueueCore> core; + BufferQueueDefs::SlotsType& slots; + u32 sticky_transform{}; + std::mutex callback_mutex; + s32 next_callback_ticket{}; + s32 current_callback_ticket{}; + std::condition_variable_any callback_condition; + + Service::Nvidia::NvCore::NvMap& nvmap; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_slot.h b/src/core/hle/service/nvflinger/buffer_slot.h new file mode 100644 index 000000000..0cd0e9964 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_slot.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferSlot.h + +#pragma once + +#include <memory> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/ui/fence.h" + +namespace Service::android { + +class GraphicBuffer; + +enum class BufferState : u32 { + Free = 0, + Dequeued = 1, + Queued = 2, + Acquired = 3, +}; + +struct BufferSlot final { + constexpr BufferSlot() = default; + + std::shared_ptr<GraphicBuffer> graphic_buffer; + BufferState buffer_state{BufferState::Free}; + bool request_buffer_called{}; + u64 frame_number{}; + Fence fence; + bool acquire_called{}; + bool attached_by_consumer{}; + bool is_preallocated{}; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_transform_flags.h b/src/core/hle/service/nvflinger/buffer_transform_flags.h new file mode 100644 index 000000000..67aa5dad6 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_transform_flags.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Service::android { + +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 + Rotate180 = 0x03, + /// Rotate source image 270 degrees clockwise + Rotate270 = 0x07, +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/consumer_base.cpp b/src/core/hle/service/nvflinger/consumer_base.cpp new file mode 100644 index 000000000..5b9995854 --- /dev/null +++ b/src/core/hle/service/nvflinger/consumer_base.cpp @@ -0,0 +1,133 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/ConsumerBase.cpp + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/service/nvflinger/buffer_item.h" +#include "core/hle/service/nvflinger/buffer_queue_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_core.h" +#include "core/hle/service/nvflinger/consumer_base.h" +#include "core/hle/service/nvflinger/ui/graphic_buffer.h" + +namespace Service::android { + +ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_) + : consumer{std::move(consumer_)} {} + +ConsumerBase::~ConsumerBase() { + std::scoped_lock lock{mutex}; + + ASSERT_MSG(is_abandoned, "consumer is not abandoned!"); +} + +void ConsumerBase::Connect(bool controlled_by_app) { + consumer->Connect(shared_from_this(), controlled_by_app); +} + +void ConsumerBase::FreeBufferLocked(s32 slot_index) { + LOG_DEBUG(Service_NVFlinger, "slot_index={}", slot_index); + + slots[slot_index].graphic_buffer = nullptr; + slots[slot_index].fence = Fence::NoFence(); + slots[slot_index].frame_number = 0; +} + +void ConsumerBase::OnFrameAvailable(const BufferItem& item) { + LOG_DEBUG(Service_NVFlinger, "called"); +} + +void ConsumerBase::OnFrameReplaced(const BufferItem& item) { + LOG_DEBUG(Service_NVFlinger, "called"); +} + +void ConsumerBase::OnBuffersReleased() { + std::scoped_lock lock{mutex}; + + LOG_DEBUG(Service_NVFlinger, "called"); + + if (is_abandoned) { + // Nothing to do if we're already abandoned. + return; + } + + u64 mask = 0; + consumer->GetReleasedBuffers(&mask); + for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) { + if (mask & (1ULL << i)) { + FreeBufferLocked(i); + } + } +} + +void ConsumerBase::OnSidebandStreamChanged() {} + +Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when) { + Status err = consumer->AcquireBuffer(item, present_when); + if (err != Status::NoError) { + return err; + } + + if (item->graphic_buffer != nullptr) { + slots[item->slot].graphic_buffer = item->graphic_buffer; + } + + slots[item->slot].frame_number = item->frame_number; + slots[item->slot].fence = item->fence; + + LOG_DEBUG(Service_NVFlinger, "slot={}", item->slot); + + return Status::NoError; +} + +Status ConsumerBase::AddReleaseFenceLocked(s32 slot, + const std::shared_ptr<GraphicBuffer> graphic_buffer, + const Fence& fence) { + LOG_DEBUG(Service_NVFlinger, "slot={}", slot); + + // If consumer no longer tracks this graphic_buffer, we can safely + // drop this fence, as it will never be received by the producer. + + if (!StillTracking(slot, graphic_buffer)) { + return Status::NoError; + } + + slots[slot].fence = fence; + + return Status::NoError; +} + +Status ConsumerBase::ReleaseBufferLocked(s32 slot, + const std::shared_ptr<GraphicBuffer> graphic_buffer) { + // If consumer no longer tracks this graphic_buffer (we received a new + // buffer on the same slot), the buffer producer is definitely no longer + // tracking it. + + if (!StillTracking(slot, graphic_buffer)) { + return Status::NoError; + } + + LOG_DEBUG(Service_NVFlinger, "slot={}", slot); + Status err = consumer->ReleaseBuffer(slot, slots[slot].frame_number, slots[slot].fence); + if (err == Status::StaleBufferSlot) { + FreeBufferLocked(slot); + } + + slots[slot].fence = Fence::NoFence(); + + return err; +} + +bool ConsumerBase::StillTracking(s32 slot, + const std::shared_ptr<GraphicBuffer> graphic_buffer) const { + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + return false; + } + + return (slots[slot].graphic_buffer != nullptr && + slots[slot].graphic_buffer->Handle() == graphic_buffer->Handle()); +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/consumer_base.h b/src/core/hle/service/nvflinger/consumer_base.h new file mode 100644 index 000000000..90ba07f45 --- /dev/null +++ b/src/core/hle/service/nvflinger/consumer_base.h @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/ConsumerBase.h + +#pragma once + +#include <array> +#include <chrono> +#include <memory> +#include <mutex> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/buffer_queue_defs.h" +#include "core/hle/service/nvflinger/consumer_listener.h" +#include "core/hle/service/nvflinger/status.h" + +namespace Service::android { + +class BufferItem; +class BufferQueueConsumer; + +class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> { +public: + void Connect(bool controlled_by_app); + +protected: + explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_); + virtual ~ConsumerBase(); + + virtual void OnFrameAvailable(const BufferItem& item) override; + virtual void OnFrameReplaced(const BufferItem& item) override; + virtual void OnBuffersReleased() override; + virtual void OnSidebandStreamChanged() override; + + void FreeBufferLocked(s32 slot_index); + Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when); + Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer); + bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer) const; + Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer, + const Fence& fence); + + struct Slot final { + std::shared_ptr<GraphicBuffer> graphic_buffer; + Fence fence; + u64 frame_number{}; + }; + +protected: + std::array<Slot, BufferQueueDefs::NUM_BUFFER_SLOTS> slots; + + bool is_abandoned{}; + + std::unique_ptr<BufferQueueConsumer> consumer; + + mutable std::mutex mutex; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/consumer_listener.h b/src/core/hle/service/nvflinger/consumer_listener.h new file mode 100644 index 000000000..74a193988 --- /dev/null +++ b/src/core/hle/service/nvflinger/consumer_listener.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IConsumerListener.h + +#pragma once + +namespace Service::android { + +class BufferItem; + +/// ConsumerListener is the interface through which the BufferQueue notifies the consumer of events +/// that the consumer may wish to react to. +class IConsumerListener { +public: + IConsumerListener() = default; + virtual ~IConsumerListener() = default; + + virtual void OnFrameAvailable(const BufferItem& item) = 0; + virtual void OnFrameReplaced(const BufferItem& item) = 0; + virtual void OnBuffersReleased() = 0; + virtual void OnSidebandStreamChanged() = 0; +}; + +}; // namespace Service::android diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp new file mode 100644 index 000000000..4043c91f1 --- /dev/null +++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp + +#include "core/hle/service/nvflinger/graphic_buffer_producer.h" +#include "core/hle/service/nvflinger/parcel.h" + +namespace Service::android { + +QueueBufferInput::QueueBufferInput(Parcel& parcel) { + parcel.ReadFlattened(*this); +} + +QueueBufferOutput::QueueBufferOutput() = default; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.h b/src/core/hle/service/nvflinger/graphic_buffer_producer.h new file mode 100644 index 000000000..6ea327bbe --- /dev/null +++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.h @@ -0,0 +1,76 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/math_util.h" +#include "core/hle/service/nvflinger/ui/fence.h" +#include "core/hle/service/nvflinger/window.h" + +namespace Service::android { + +class Parcel; + +#pragma pack(push, 1) +struct QueueBufferInput final { + explicit QueueBufferInput(Parcel& parcel); + + void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_, + NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_, + u32* sticky_transform_, bool* async_, s32* swap_interval_, Fence* fence_) const { + *timestamp_ = timestamp; + *is_auto_timestamp_ = static_cast<bool>(is_auto_timestamp); + *crop_ = crop; + *scaling_mode_ = scaling_mode; + *transform_ = transform; + *sticky_transform_ = sticky_transform; + *async_ = static_cast<bool>(async); + *swap_interval_ = swap_interval; + *fence_ = fence; + } + +private: + s64 timestamp{}; + s32 is_auto_timestamp{}; + Common::Rectangle<s32> crop{}; + NativeWindowScalingMode scaling_mode{}; + NativeWindowTransform transform{}; + u32 sticky_transform{}; + s32 async{}; + s32 swap_interval{}; + Fence fence{}; +}; +#pragma pack(pop) +static_assert(sizeof(QueueBufferInput) == 84, "QueueBufferInput has wrong size"); + +struct QueueBufferOutput final { + QueueBufferOutput(); + + void Deflate(u32* width_, u32* height_, u32* transform_hint_, u32* num_pending_buffers_) const { + *width_ = width; + *height_ = height; + *transform_hint_ = transform_hint; + *num_pending_buffers_ = num_pending_buffers; + } + + void Inflate(u32 width_, u32 height_, u32 transform_hint_, u32 num_pending_buffers_) { + width = width_; + height = height_; + transform_hint = transform_hint_; + num_pending_buffers = num_pending_buffers_; + } + +private: + u32 width{}; + u32 height{}; + u32 transform_hint{}; + u32 num_pending_buffers{}; +}; +static_assert(sizeof(QueueBufferOutput) == 16, "QueueBufferOutput has wrong size"); + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp new file mode 100644 index 000000000..dc9b2a9ec --- /dev/null +++ b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <mutex> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/hos_binder_driver_server.h" + +namespace Service::NVFlinger { + +HosBinderDriverServer::HosBinderDriverServer(Core::System& system_) + : service_context(system_, "HosBinderDriverServer") {} + +HosBinderDriverServer::~HosBinderDriverServer() {} + +u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) { + std::scoped_lock lk{lock}; + + last_id++; + + producers[last_id] = std::move(binder); + + return last_id; +} + +android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) { + std::scoped_lock lk{lock}; + + if (auto search = producers.find(id); search != producers.end()) { + return search->second.get(); + } + + return {}; +} + +} // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/hos_binder_driver_server.h b/src/core/hle/service/nvflinger/hos_binder_driver_server.h new file mode 100644 index 000000000..8fddc1206 --- /dev/null +++ b/src/core/hle/service/nvflinger/hos_binder_driver_server.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <memory> +#include <mutex> +#include <unordered_map> + +#include "common/common_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nvflinger/binder.h" + +namespace Core { +class System; +} + +namespace Service::NVFlinger { + +class HosBinderDriverServer final { +public: + explicit HosBinderDriverServer(Core::System& system_); + ~HosBinderDriverServer(); + + u64 RegisterProducer(std::unique_ptr<android::IBinder>&& binder); + + android::IBinder* TryGetProducer(u64 id); + +private: + KernelHelpers::ServiceContext service_context; + + std::unordered_map<u64, std::unique_ptr<android::IBinder>> producers; + std::mutex lock; + u64 last_id{}; +}; + +} // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 01e69de30..aa14d2cbc 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #include <algorithm> #include <optional> @@ -16,11 +15,17 @@ #include "core/hle/kernel/k_readable_event.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/buffer_item_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_core.h" +#include "core/hle/service/nvflinger/hos_binder_driver_server.h" #include "core/hle/service/nvflinger/nvflinger.h" +#include "core/hle/service/nvflinger/ui/graphic_buffer.h" #include "core/hle/service/vi/display/vi_display.h" #include "core/hle/service/vi/layer/vi_layer.h" +#include "core/hle/service/vi/vi_results.h" #include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" +#include "video_core/host1x/syncpoint_manager.h" namespace Service::NVFlinger { @@ -28,7 +33,7 @@ constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60}; void NVFlinger::SplitVSync(std::stop_token stop_token) { system.RegisterHostThread(); - std::string name = "yuzu:VSyncThread"; + std::string name = "VSyncThread"; MicroProfileOnThreadCreate(name.c_str()); // Cleanup @@ -36,69 +41,87 @@ void NVFlinger::SplitVSync(std::stop_token stop_token) { Common::SetCurrentThreadName(name.c_str()); Common::SetCurrentThreadPriority(Common::ThreadPriority::High); - s64 delay = 0; + while (!stop_token.stop_requested()) { + vsync_signal.wait(false); + vsync_signal.store(false); + guard->lock(); - const s64 time_start = system.CoreTiming().GetGlobalTimeNs().count(); + Compose(); - const auto ticks = GetNextTicks(); - const s64 time_end = system.CoreTiming().GetGlobalTimeNs().count(); - const s64 time_passed = time_end - time_start; - const s64 next_time = std::max<s64>(0, ticks - time_passed - delay); + guard->unlock(); - if (next_time > 0) { - std::this_thread::sleep_for(std::chrono::nanoseconds{next_time}); - } - delay = (system.CoreTiming().GetGlobalTimeNs().count() - time_end) - next_time; } } -NVFlinger::NVFlinger(Core::System& system_) - : system(system_), service_context(system_, "nvflinger") { - displays.emplace_back(0, "Default", service_context, system); - displays.emplace_back(1, "External", service_context, system); - displays.emplace_back(2, "Edid", service_context, system); - displays.emplace_back(3, "Internal", service_context, system); - displays.emplace_back(4, "Null", service_context, system); +NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_) + : system(system_), service_context(system_, "nvflinger"), + hos_binder_driver_server(hos_binder_driver_server_) { + displays.emplace_back(0, "Default", hos_binder_driver_server, service_context, system); + displays.emplace_back(1, "External", hos_binder_driver_server, service_context, system); + displays.emplace_back(2, "Edid", hos_binder_driver_server, service_context, system); + displays.emplace_back(3, "Internal", hos_binder_driver_server, service_context, system); + displays.emplace_back(4, "Null", hos_binder_driver_server, service_context, system); guard = std::make_shared<std::mutex>(); // Schedule the screen composition events - composition_event = Core::Timing::CreateEvent( - "ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { + multi_composition_event = Core::Timing::CreateEvent( + "ScreenComposition", + [this](std::uintptr_t, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + vsync_signal.store(true); + vsync_signal.notify_all(); + return std::chrono::nanoseconds(GetNextTicks()); + }); + + single_composition_event = Core::Timing::CreateEvent( + "ScreenComposition", + [this](std::uintptr_t, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto lock_guard = Lock(); Compose(); - const auto ticks = std::chrono::nanoseconds{GetNextTicks()}; - const auto ticks_delta = ticks - ns_late; - const auto future_ns = std::max(std::chrono::nanoseconds::zero(), ticks_delta); - - this->system.CoreTiming().ScheduleEvent(future_ns, composition_event); + return std::chrono::nanoseconds(GetNextTicks()); }); if (system.IsMulticore()) { + system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, multi_composition_event); vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); }); } else { - system.CoreTiming().ScheduleEvent(frame_ns, composition_event); + system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, single_composition_event); } } NVFlinger::~NVFlinger() { - for (auto& buffer_queue : buffer_queues) { - buffer_queue->Disconnect(); + if (system.IsMulticore()) { + system.CoreTiming().UnscheduleEvent(multi_composition_event, {}); + vsync_thread.request_stop(); + vsync_signal.store(true); + vsync_signal.notify_all(); + } else { + system.CoreTiming().UnscheduleEvent(single_composition_event, {}); } - if (!system.IsMulticore()) { - system.CoreTiming().UnscheduleEvent(composition_event, 0); + + for (auto& display : displays) { + for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { + display.GetLayer(layer).Core().NotifyShutdown(); + } + } + + if (nvdrv) { + nvdrv->Close(disp_fd); } } void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { nvdrv = std::move(instance); + disp_fd = nvdrv->Open("/dev/nvdisp_disp0"); } std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { const auto lock_guard = Lock(); - LOG_DEBUG(Service, "Opening \"{}\" display", name); + LOG_DEBUG(Service_NVFlinger, "Opening \"{}\" display", name); const auto itr = std::find_if(displays.begin(), displays.end(), @@ -125,10 +148,8 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { } void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { - const u32 buffer_queue_id = next_buffer_queue_id++; - buffer_queues.emplace_back( - std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context)); - display.CreateLayer(layer_id, *buffer_queues.back()); + const auto buffer_id = next_buffer_queue_id++; + display.CreateLayer(layer_id, buffer_id, nvdrv->container); } void NVFlinger::CloseLayer(u64 layer_id) { @@ -147,30 +168,18 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) { return std::nullopt; } - return layer->GetBufferQueue().GetId(); + return layer->GetBinderId(); } -Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) { +ResultVal<Kernel::KReadableEvent*> NVFlinger::FindVsyncEvent(u64 display_id) { const auto lock_guard = Lock(); auto* const display = FindDisplay(display_id); if (display == nullptr) { - return nullptr; + return VI::ResultNotFound; } - return &display->GetVSyncEvent(); -} - -BufferQueue* NVFlinger::FindBufferQueue(u32 id) { - const auto lock_guard = Lock(); - const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), - [id](const auto& queue) { return queue->GetId() == id; }); - - if (itr == buffer_queues.end()) { - return nullptr; - } - - return itr->get(); + return display->GetVSyncEvent(); } VI::Display* NVFlinger::FindDisplay(u64 display_id) { @@ -227,7 +236,7 @@ VI::Layer* NVFlinger::FindOrCreateLayer(u64 display_id, u64 layer_id) { auto* layer = display->FindLayer(layer_id); if (layer == nullptr) { - LOG_DEBUG(Service, "Layer at id {} not found. Trying to create it.", layer_id); + LOG_DEBUG(Service_NVFlinger, "Layer at id {} not found. Trying to create it.", layer_id); CreateLayerAtId(*display, layer_id); return display->FindLayer(layer_id); } @@ -246,44 +255,43 @@ void NVFlinger::Compose() { // TODO(Subv): Support more than 1 layer. VI::Layer& layer = display.GetLayer(0); - auto& buffer_queue = layer.GetBufferQueue(); - // Search for a queued buffer and acquire it - auto buffer = buffer_queue.AcquireBuffer(); + android::BufferItem buffer{}; + const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false); - if (!buffer) { + if (status != android::Status::NoError) { continue; } - const auto& igbp_buffer = buffer->get().igbp_buffer; + const auto& igbp_buffer = *buffer.graphic_buffer; if (!system.IsPoweredOn()) { return; // We are likely shutting down } - auto& gpu = system.GPU(); - const auto& multi_fence = buffer->get().multi_fence; - guard->unlock(); - for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { - const auto& fence = multi_fence.fences[fence_id]; - gpu.WaitFence(fence.id, fence.value); - } - guard->lock(); - - MicroProfileFlip(); - // Now send the buffer to the GPU for drawing. // 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"); + auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd); ASSERT(nvdisp); - nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.external_format, - igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, - buffer->get().transform, buffer->get().crop_rect); + guard->unlock(); + Common::Rectangle<int> crop_rect{ + static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()), + static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())}; - swap_interval = buffer->get().swap_interval; - buffer_queue.ReleaseBuffer(buffer->get().slot); + nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(), + igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(), + static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect, + buffer.fence.fences, buffer.fence.num_fences); + + MicroProfileFlip(); + guard->lock(); + + swap_interval = buffer.swap_interval; + + auto fence = android::Fence::NoFence(); + layer.GetConsumer().ReleaseBuffer(buffer, fence); } } @@ -291,9 +299,21 @@ s64 NVFlinger::GetNextTicks() const { static constexpr s64 max_hertz = 120LL; const auto& settings = Settings::values; - const bool unlocked_fps = settings.disable_fps_limit.GetValue(); - const s64 fps_cap = unlocked_fps ? static_cast<s64>(settings.fps_cap.GetValue()) : 1; - return (1000000000 * (1LL << swap_interval)) / (max_hertz * fps_cap); + auto speed_scale = 1.f; + if (settings.use_multi_core.GetValue()) { + if (settings.use_speed_limit.GetValue()) { + // Scales the speed based on speed_limit setting on MC. SC is handled by + // SpeedLimiter::DoSpeedLimiting. + speed_scale = 100.f / settings.speed_limit.GetValue(); + } else { + // Run at unlocked framerate. + speed_scale = 0.01f; + } + } + + const auto next_ticks = ((1000000000 * (1LL << swap_interval)) / max_hertz); + + return static_cast<s64>(speed_scale * static_cast<float>(next_ticks)); } } // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 7935cf773..b62615de2 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once @@ -12,6 +11,7 @@ #include <vector> #include "common/common_types.h" +#include "core/hle/result.h" #include "core/hle/service/kernel_helpers.h" namespace Common { @@ -37,13 +37,16 @@ class Display; class Layer; } // namespace Service::VI -namespace Service::NVFlinger { +namespace Service::android { +class BufferQueueCore; +class BufferQueueProducer; +} // namespace Service::android -class BufferQueue; +namespace Service::NVFlinger { class NVFlinger final { public: - explicit NVFlinger(Core::System& system_); + explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_); ~NVFlinger(); /// Sets the NVDrv module instance to use to send buffers to the GPU. @@ -69,11 +72,9 @@ public: /// Gets the vsync event for the specified display. /// - /// If an invalid display ID is provided, then nullptr is returned. - [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id); - - /// Obtains a buffer queue identified by the ID. - [[nodiscard]] BufferQueue* FindBufferQueue(u32 id); + /// If an invalid display ID is provided, then VI::ResultNotFound is returned. + /// If the vsync event has already been retrieved, then VI::ResultPermissionDenied is returned. + [[nodiscard]] ResultVal<Kernel::KReadableEvent*> FindVsyncEvent(u64 display_id); /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when /// finished. @@ -82,6 +83,12 @@ public: [[nodiscard]] s64 GetNextTicks() const; private: + struct Layer { + std::unique_ptr<android::BufferQueueCore> core; + std::unique_ptr<android::BufferQueueProducer> producer; + }; + +private: [[nodiscard]] std::unique_lock<std::mutex> Lock() const { return std::unique_lock{*guard}; } @@ -109,9 +116,9 @@ private: void SplitVSync(std::stop_token stop_token); std::shared_ptr<Nvidia::Module> nvdrv; + s32 disp_fd; std::list<VI::Display> displays; - std::vector<std::unique_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; @@ -122,15 +129,20 @@ private: u32 swap_interval = 1; /// Event that handles screen composition. - std::shared_ptr<Core::Timing::EventType> composition_event; + std::shared_ptr<Core::Timing::EventType> multi_composition_event; + std::shared_ptr<Core::Timing::EventType> single_composition_event; std::shared_ptr<std::mutex> guard; Core::System& system; + std::atomic<bool> vsync_signal; + std::jthread vsync_thread; KernelHelpers::ServiceContext service_context; + + HosBinderDriverServer& hos_binder_driver_server; }; } // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/parcel.h b/src/core/hle/service/nvflinger/parcel.h new file mode 100644 index 000000000..f3fa2587d --- /dev/null +++ b/src/core/hle/service/nvflinger/parcel.h @@ -0,0 +1,172 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <memory> +#include <vector> + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/common_types.h" + +namespace Service::android { + +class Parcel final { +public: + static constexpr std::size_t DefaultBufferSize = 0x40; + + Parcel() : buffer(DefaultBufferSize) {} + + template <typename T> + explicit Parcel(const T& out_data) : buffer(DefaultBufferSize) { + Write(out_data); + } + + explicit Parcel(std::vector<u8> in_data) : buffer(std::move(in_data)) { + DeserializeHeader(); + [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); + } + + template <typename T> + void Read(T& val) { + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); + ASSERT(read_index + sizeof(T) <= buffer.size()); + + std::memcpy(&val, buffer.data() + read_index, sizeof(T)); + read_index += sizeof(T); + read_index = Common::AlignUp(read_index, 4); + } + + template <typename T> + T Read() { + T val; + Read(val); + return val; + } + + template <typename T> + void ReadFlattened(T& val) { + const auto flattened_size = Read<s64>(); + ASSERT(sizeof(T) == flattened_size); + Read(val); + } + + template <typename T> + T ReadFlattened() { + T val; + ReadFlattened(val); + return val; + } + + template <typename T> + T ReadUnaligned() { + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); + ASSERT(read_index + sizeof(T) <= buffer.size()); + + T val; + std::memcpy(&val, buffer.data() + read_index, sizeof(T)); + read_index += sizeof(T); + return val; + } + + template <typename T> + const std::shared_ptr<T> ReadObject() { + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); + + const auto is_valid{Read<bool>()}; + + if (is_valid) { + auto result = std::make_shared<T>(); + ReadFlattened(*result); + return result; + } + + return {}; + } + + std::u16string ReadInterfaceToken() { + [[maybe_unused]] const u32 unknown = Read<u32>(); + const u32 length = Read<u32>(); + + std::u16string token; + token.reserve(length + 1); + + for (u32 ch = 0; ch < length + 1; ++ch) { + token.push_back(ReadUnaligned<u16>()); + } + + read_index = Common::AlignUp(read_index, 4); + + return token; + } + + template <typename T> + void Write(const T& val) { + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); + + if (buffer.size() < write_index + sizeof(T)) { + buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize); + } + + std::memcpy(buffer.data() + write_index, &val, sizeof(T)); + write_index += sizeof(T); + write_index = Common::AlignUp(write_index, 4); + } + + template <typename T> + void WriteObject(const T* ptr) { + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); + + if (!ptr) { + Write<u32>(0); + return; + } + + Write<u32>(1); + Write<s64>(sizeof(T)); + Write(*ptr); + } + + template <typename T> + void WriteObject(const std::shared_ptr<T> ptr) { + WriteObject(ptr.get()); + } + + void DeserializeHeader() { + ASSERT(buffer.size() > sizeof(Header)); + + Header header{}; + std::memcpy(&header, buffer.data(), sizeof(Header)); + + read_index = header.data_offset; + } + + std::vector<u8> Serialize() const { + ASSERT(read_index == 0); + + Header header{}; + header.data_size = static_cast<u32>(write_index - sizeof(Header)); + header.data_offset = sizeof(Header); + header.objects_size = 4; + header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size); + std::memcpy(buffer.data(), &header, sizeof(Header)); + + return buffer; + } + +private: + struct Header { + u32 data_size; + u32 data_offset; + u32 objects_size; + u32 objects_offset; + }; + static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size"); + + mutable std::vector<u8> buffer; + std::size_t read_index = 0; + std::size_t write_index = sizeof(Header); +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/pixel_format.h b/src/core/hle/service/nvflinger/pixel_format.h new file mode 100644 index 000000000..f77d0acfb --- /dev/null +++ b/src/core/hle/service/nvflinger/pixel_format.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Service::android { + +enum class PixelFormat : u32 { + NoFormat = 0, + Rgba8888 = 1, + Rgbx8888 = 2, + Rgb888 = 3, + Rgb565 = 4, + Bgra8888 = 5, + Rgba5551 = 6, + Rgba4444 = 7, +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/producer_listener.h b/src/core/hle/service/nvflinger/producer_listener.h new file mode 100644 index 000000000..1c4d5db0e --- /dev/null +++ b/src/core/hle/service/nvflinger/producer_listener.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IProducerListener.h + +#pragma once + +namespace Service::android { + +class IProducerListener { +public: + virtual void OnBufferReleased() = 0; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/status.h b/src/core/hle/service/nvflinger/status.h new file mode 100644 index 000000000..7af166c40 --- /dev/null +++ b/src/core/hle/service/nvflinger/status.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Service::android { + +enum class Status : s32 { + None = 0, + NoError = 0, + StaleBufferSlot = 1, + NoBufferAvailable = 2, + PresentLater = 3, + WouldBlock = -11, + NoMemory = -12, + Busy = -16, + NoInit = -19, + BadValue = -22, + InvalidOperation = -37, + BufferNeedsReallocation = 1, + ReleaseAllBuffers = 2, +}; +DECLARE_ENUM_FLAG_OPERATORS(Status); + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/ui/fence.h b/src/core/hle/service/nvflinger/ui/fence.h new file mode 100644 index 000000000..536e8156d --- /dev/null +++ b/src/core/hle/service/nvflinger/ui/fence.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/Fence.h + +#pragma once + +#include <array> + +#include "common/common_types.h" +#include "core/hle/service/nvdrv/nvdata.h" + +namespace Service::android { + +class Fence { +public: + constexpr Fence() = default; + + static constexpr Fence NoFence() { + Fence fence; + fence.fences[0].id = -1; + return fence; + } + +public: + u32 num_fences{}; + std::array<Service::Nvidia::NvFence, 4> fences{}; +}; +static_assert(sizeof(Fence) == 36, "Fence has wrong size"); + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/ui/graphic_buffer.h b/src/core/hle/service/nvflinger/ui/graphic_buffer.h new file mode 100644 index 000000000..9a27f8f02 --- /dev/null +++ b/src/core/hle/service/nvflinger/ui/graphic_buffer.h @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2007 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/GraphicBuffer.h + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/service/nvflinger/pixel_format.h" + +namespace Service::android { + +class GraphicBuffer final { +public: + constexpr GraphicBuffer() = default; + + constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) + : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_}, + usage{static_cast<s32>(usage_)} {} + + constexpr u32 Width() const { + return static_cast<u32>(width); + } + + constexpr u32 Height() const { + return static_cast<u32>(height); + } + + constexpr u32 Stride() const { + return static_cast<u32>(stride); + } + + constexpr u32 Usage() const { + return static_cast<u32>(usage); + } + + constexpr PixelFormat Format() const { + return format; + } + + constexpr u32 BufferId() const { + return buffer_id; + } + + constexpr PixelFormat ExternalFormat() const { + return external_format; + } + + constexpr u32 Handle() const { + return handle; + } + + constexpr u32 Offset() const { + return offset; + } + + constexpr bool NeedsReallocation(u32 width_, u32 height_, PixelFormat format_, + u32 usage_) const { + if (static_cast<s32>(width_) != width) { + return true; + } + + if (static_cast<s32>(height_) != height) { + return true; + } + + if (format_ != format) { + return true; + } + + if ((static_cast<u32>(usage) & usage_) != usage_) { + return true; + } + + return false; + } + +private: + u32 magic{}; + s32 width{}; + s32 height{}; + s32 stride{}; + PixelFormat format{}; + s32 usage{}; + INSERT_PADDING_WORDS(1); + u32 index{}; + INSERT_PADDING_WORDS(3); + u32 buffer_id{}; + INSERT_PADDING_WORDS(6); + PixelFormat external_format{}; + INSERT_PADDING_WORDS(10); + u32 handle{}; + u32 offset{}; + INSERT_PADDING_WORDS(60); +}; +static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size"); + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/window.h b/src/core/hle/service/nvflinger/window.h new file mode 100644 index 000000000..61cca5b01 --- /dev/null +++ b/src/core/hle/service/nvflinger/window.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Service::android { + +/// Attributes queryable with Query +enum class NativeWindow : s32 { + Width = 0, + Height = 1, + Format = 2, + MinUndequeedBuffers = 3, + QueuesToWindowComposer = 4, + ConcreteType = 5, + DefaultWidth = 6, + DefaultHeight = 7, + TransformHint = 8, + ConsumerRunningBehind = 9, + ConsumerUsageBits = 10, + StickyTransform = 11, + DefaultDataSpace = 12, + BufferAge = 13, +}; + +/// Parameter for Connect/Disconnect +enum class NativeWindowApi : s32 { + NoConnectedApi = 0, + Egl = 1, + Cpu = 2, + Media = 3, + Camera = 4, +}; + +/// Scaling mode parameter for QueueBuffer +enum class NativeWindowScalingMode : s32 { + Freeze = 0, + ScaleToWindow = 1, + ScaleCrop = 2, + NoScaleCrop = 3, +}; + +/// Transform parameter for QueueBuffer +enum class NativeWindowTransform : u32 { + None = 0x0, + InverseDisplay = 0x08, +}; +DECLARE_ENUM_FLAG_OPERATORS(NativeWindowTransform); + +} // namespace Service::android diff --git a/src/core/hle/service/olsc/olsc.cpp b/src/core/hle/service/olsc/olsc.cpp index 39a8031a5..530e1be3b 100644 --- a/src/core/hle/service/olsc/olsc.cpp +++ b/src/core/hle/service/olsc/olsc.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/ipc_helpers.h" #include "core/hle/service/olsc/olsc.h" diff --git a/src/core/hle/service/olsc/olsc.h b/src/core/hle/service/olsc/olsc.h index 24f24ca6b..1522d8d32 100644 --- a/src/core/hle/service/olsc/olsc.h +++ b/src/core/hle/service/olsc/olsc.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/pcie/pcie.cpp b/src/core/hle/service/pcie/pcie.cpp index 9bc851591..79501b9f9 100644 --- a/src/core/hle/service/pcie/pcie.cpp +++ b/src/core/hle/service/pcie/pcie.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/pcie/pcie.h b/src/core/hle/service/pcie/pcie.h index e5709a72f..cebfd9042 100644 --- a/src/core/hle/service/pcie/pcie.h +++ b/src/core/hle/service/pcie/pcie.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/pctl/pctl.cpp b/src/core/hle/service/pctl/pctl.cpp index 908e0a1e3..3f47bf094 100644 --- a/src/core/hle/service/pctl/pctl.cpp +++ b/src/core/hle/service/pctl/pctl.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/pctl/pctl.h" diff --git a/src/core/hle/service/pctl/pctl.h b/src/core/hle/service/pctl/pctl.h index 1d28900b2..87f93161e 100644 --- a/src/core/hle/service/pctl/pctl.h +++ b/src/core/hle/service/pctl/pctl.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/pctl/pctl_module.cpp b/src/core/hle/service/pctl/pctl_module.cpp index 240776101..2a123b42d 100644 --- a/src/core/hle/service/pctl/pctl_module.cpp +++ b/src/core/hle/service/pctl/pctl_module.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/core.h" @@ -14,10 +13,10 @@ namespace Service::PCTL { namespace Error { -constexpr ResultCode ResultNoFreeCommunication{ErrorModule::PCTL, 101}; -constexpr ResultCode ResultStereoVisionRestricted{ErrorModule::PCTL, 104}; -constexpr ResultCode ResultNoCapability{ErrorModule::PCTL, 131}; -constexpr ResultCode ResultNoRestrictionEnabled{ErrorModule::PCTL, 181}; +constexpr Result ResultNoFreeCommunication{ErrorModule::PCTL, 101}; +constexpr Result ResultStereoVisionRestricted{ErrorModule::PCTL, 104}; +constexpr Result ResultNoCapability{ErrorModule::PCTL, 131}; +constexpr Result ResultNoRestrictionEnabled{ErrorModule::PCTL, 181}; } // namespace Error diff --git a/src/core/hle/service/pctl/pctl_module.h b/src/core/hle/service/pctl/pctl_module.h index f25c5c557..6f584530d 100644 --- a/src/core/hle/service/pctl/pctl_module.h +++ b/src/core/hle/service/pctl/pctl_module.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/pcv/pcv.cpp b/src/core/hle/service/pcv/pcv.cpp index 68b2c4178..f7a497a14 100644 --- a/src/core/hle/service/pcv/pcv.cpp +++ b/src/core/hle/service/pcv/pcv.cpp @@ -1,9 +1,9 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> +#include "core/hle/ipc_helpers.h" #include "core/hle/service/pcv/pcv.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" @@ -78,10 +78,102 @@ public: } }; +class IClkrstSession final : public ServiceFramework<IClkrstSession> { +public: + explicit IClkrstSession(Core::System& system_, DeviceCode deivce_code_) + : ServiceFramework{system_, "IClkrstSession"}, deivce_code(deivce_code_) { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "SetClockEnabled"}, + {1, nullptr, "SetClockDisabled"}, + {2, nullptr, "SetResetAsserted"}, + {3, nullptr, "SetResetDeasserted"}, + {4, nullptr, "SetPowerEnabled"}, + {5, nullptr, "SetPowerDisabled"}, + {6, nullptr, "GetState"}, + {7, &IClkrstSession::SetClockRate, "SetClockRate"}, + {8, &IClkrstSession::GetClockRate, "GetClockRate"}, + {9, nullptr, "SetMinVClockRate"}, + {10, nullptr, "GetPossibleClockRates"}, + {11, nullptr, "GetDvfsTable"}, + }; + // clang-format on + RegisterHandlers(functions); + } + +private: + void SetClockRate(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + clock_rate = rp.Pop<u32>(); + LOG_DEBUG(Service_PCV, "(STUBBED) called, clock_rate={}", clock_rate); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void GetClockRate(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCV, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(clock_rate); + } + + DeviceCode deivce_code; + u32 clock_rate{}; +}; + +class CLKRST final : public ServiceFramework<CLKRST> { +public: + explicit CLKRST(Core::System& system_, const char* name) : ServiceFramework{system_, name} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &CLKRST::OpenSession, "OpenSession"}, + {1, nullptr, "GetTemperatureThresholds"}, + {2, nullptr, "SetTemperature"}, + {3, nullptr, "GetModuleStateTable"}, + {4, nullptr, "GetModuleStateTableEvent"}, + {5, nullptr, "GetModuleStateTableMaxCount"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void OpenSession(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_code = static_cast<DeviceCode>(rp.Pop<u32>()); + const auto unkonwn_input = rp.Pop<u32>(); + + LOG_DEBUG(Service_PCV, "called, device_code={}, input={}", device_code, unkonwn_input); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IClkrstSession>(system, device_code); + } +}; + +class CLKRST_A final : public ServiceFramework<CLKRST_A> { +public: + explicit CLKRST_A(Core::System& system_) : ServiceFramework{system_, "clkrst:a"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "ReleaseControl"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { std::make_shared<PCV>(system)->InstallAsService(sm); std::make_shared<PCV_ARB>(system)->InstallAsService(sm); std::make_shared<PCV_IMM>(system)->InstallAsService(sm); + std::make_shared<CLKRST>(system, "clkrst")->InstallAsService(sm); + std::make_shared<CLKRST>(system, "clkrst:i")->InstallAsService(sm); + std::make_shared<CLKRST_A>(system)->InstallAsService(sm); } } // namespace Service::PCV diff --git a/src/core/hle/service/pcv/pcv.h b/src/core/hle/service/pcv/pcv.h index c61a0b591..6b26b6fa7 100644 --- a/src/core/hle/service/pcv/pcv.h +++ b/src/core/hle/service/pcv/pcv.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -14,6 +13,97 @@ class ServiceManager; namespace Service::PCV { +enum class DeviceCode : u32 { + Cpu = 0x40000001, + Gpu = 0x40000002, + I2s1 = 0x40000003, + I2s2 = 0x40000004, + I2s3 = 0x40000005, + Pwm = 0x40000006, + I2c1 = 0x02000001, + I2c2 = 0x02000002, + I2c3 = 0x02000003, + I2c4 = 0x02000004, + I2c5 = 0x02000005, + I2c6 = 0x02000006, + Spi1 = 0x07000000, + Spi2 = 0x07000001, + Spi3 = 0x07000002, + Spi4 = 0x07000003, + Disp1 = 0x40000011, + Disp2 = 0x40000012, + Isp = 0x40000013, + Vi = 0x40000014, + Sdmmc1 = 0x40000015, + Sdmmc2 = 0x40000016, + Sdmmc3 = 0x40000017, + Sdmmc4 = 0x40000018, + Owr = 0x40000019, + Csite = 0x4000001A, + Tsec = 0x4000001B, + Mselect = 0x4000001C, + Hda2codec2x = 0x4000001D, + Actmon = 0x4000001E, + I2cSlow = 0x4000001F, + Sor1 = 0x40000020, + Sata = 0x40000021, + Hda = 0x40000022, + XusbCoreHostSrc = 0x40000023, + XusbFalconSrc = 0x40000024, + XusbFsSrc = 0x40000025, + XusbCoreDevSrc = 0x40000026, + XusbSsSrc = 0x40000027, + UartA = 0x03000001, + UartB = 0x35000405, + UartC = 0x3500040F, + UartD = 0x37000001, + Host1x = 0x4000002C, + Entropy = 0x4000002D, + SocTherm = 0x4000002E, + Vic = 0x4000002F, + Nvenc = 0x40000030, + Nvjpg = 0x40000031, + Nvdec = 0x40000032, + Qspi = 0x40000033, + ViI2c = 0x40000034, + Tsecb = 0x40000035, + Ape = 0x40000036, + AudioDsp = 0x40000037, + AudioUart = 0x40000038, + Emc = 0x40000039, + Plle = 0x4000003A, + PlleHwSeq = 0x4000003B, + Dsi = 0x4000003C, + Maud = 0x4000003D, + Dpaux1 = 0x4000003E, + MipiCal = 0x4000003F, + UartFstMipiCal = 0x40000040, + Osc = 0x40000041, + SysBus = 0x40000042, + SorSafe = 0x40000043, + XusbSs = 0x40000044, + XusbHost = 0x40000045, + XusbDevice = 0x40000046, + Extperiph1 = 0x40000047, + Ahub = 0x40000048, + Hda2hdmicodec = 0x40000049, + Gpuaux = 0x4000004A, + UsbD = 0x4000004B, + Usb2 = 0x4000004C, + Pcie = 0x4000004D, + Afi = 0x4000004E, + PciExClk = 0x4000004F, + PExUsbPhy = 0x40000050, + XUsbPadCtl = 0x40000051, + Apbdma = 0x40000052, + Usb2TrkClk = 0x40000053, + XUsbIoPll = 0x40000054, + XUsbIoPllHwSeq = 0x40000055, + Cec = 0x40000056, + Extperiph2 = 0x40000057, + OscClk = 0x40000080 +}; + void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); } // namespace Service::PCV diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp index 057666021..b10e86c8f 100644 --- a/src/core/hle/service/pm/pm.cpp +++ b/src/core/hle/service/pm/pm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/hle/ipc_helpers.h" @@ -13,12 +12,12 @@ namespace Service::PM { namespace { -constexpr ResultCode ResultProcessNotFound{ErrorModule::PM, 1}; -[[maybe_unused]] constexpr ResultCode ResultAlreadyStarted{ErrorModule::PM, 2}; -[[maybe_unused]] constexpr ResultCode ResultNotTerminated{ErrorModule::PM, 3}; -[[maybe_unused]] constexpr ResultCode ResultDebugHookInUse{ErrorModule::PM, 4}; -[[maybe_unused]] constexpr ResultCode ResultApplicationRunning{ErrorModule::PM, 5}; -[[maybe_unused]] constexpr ResultCode ResultInvalidSize{ErrorModule::PM, 6}; +constexpr Result ResultProcessNotFound{ErrorModule::PM, 1}; +[[maybe_unused]] constexpr Result ResultAlreadyStarted{ErrorModule::PM, 2}; +[[maybe_unused]] constexpr Result ResultNotTerminated{ErrorModule::PM, 3}; +[[maybe_unused]] constexpr Result ResultDebugHookInUse{ErrorModule::PM, 4}; +[[maybe_unused]] constexpr Result ResultApplicationRunning{ErrorModule::PM, 5}; +[[maybe_unused]] constexpr Result ResultInvalidSize{ErrorModule::PM, 6}; constexpr u64 NO_PROCESS_FOUND_PID{0}; diff --git a/src/core/hle/service/pm/pm.h b/src/core/hle/service/pm/pm.h index 852e7050c..060103928 100644 --- a/src/core/hle/service/pm/pm.h +++ b/src/core/hle/service/pm/pm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 5c8a44688..78f897d3e 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/hex_util.h" #include "common/logging/log.h" diff --git a/src/core/hle/service/prepo/prepo.h b/src/core/hle/service/prepo/prepo.h index 395b57ead..37ea5afad 100644 --- a/src/core/hle/service/prepo/prepo.h +++ b/src/core/hle/service/prepo/prepo.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/psc/psc.cpp b/src/core/hle/service/psc/psc.cpp index f8ea02b58..3a9412cf5 100644 --- a/src/core/hle/service/psc/psc.cpp +++ b/src/core/hle/service/psc/psc.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/psc/psc.h b/src/core/hle/service/psc/psc.h index 89344f32d..d248372c2 100644 --- a/src/core/hle/service/psc/psc.h +++ b/src/core/hle/service/psc/psc.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp index 5d248f671..2c31e9485 100644 --- a/src/core/hle/service/ptm/psm.cpp +++ b/src/core/hle/service/ptm/psm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> @@ -10,10 +9,8 @@ #include "core/hle/kernel/k_event.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/ptm/psm.h" -#include "core/hle/service/service.h" -#include "core/hle/service/sm/sm.h" -namespace Service::PSM { +namespace Service::PTM { class IPsmSession final : public ServiceFramework<IPsmSession> { public: @@ -58,7 +55,7 @@ public: private: void BindStateChangeEvent(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PSM, "called"); + LOG_DEBUG(Service_PTM, "called"); should_signal = true; @@ -68,7 +65,7 @@ private: } void UnbindStateChangeEvent(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PSM, "called"); + LOG_DEBUG(Service_PTM, "called"); should_signal = false; @@ -79,7 +76,7 @@ private: void SetChargerTypeChangeEventEnabled(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto state = rp.Pop<bool>(); - LOG_DEBUG(Service_PSM, "called, state={}", state); + LOG_DEBUG(Service_PTM, "called, state={}", state); should_signal_charger_type = state; @@ -90,7 +87,7 @@ private: void SetPowerSupplyChangeEventEnabled(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto state = rp.Pop<bool>(); - LOG_DEBUG(Service_PSM, "called, state={}", state); + LOG_DEBUG(Service_PTM, "called, state={}", state); should_signal_power_supply = state; @@ -101,7 +98,7 @@ private: void SetBatteryVoltageStateChangeEventEnabled(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto state = rp.Pop<bool>(); - LOG_DEBUG(Service_PSM, "called, state={}", state); + LOG_DEBUG(Service_PTM, "called, state={}", state); should_signal_battery_voltage = state; @@ -118,76 +115,58 @@ private: Kernel::KEvent* state_change_event; }; -class PSM final : public ServiceFramework<PSM> { -public: - explicit PSM(Core::System& system_) : ServiceFramework{system_, "psm"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"}, - {1, &PSM::GetChargerType, "GetChargerType"}, - {2, nullptr, "EnableBatteryCharging"}, - {3, nullptr, "DisableBatteryCharging"}, - {4, nullptr, "IsBatteryChargingEnabled"}, - {5, nullptr, "AcquireControllerPowerSupply"}, - {6, nullptr, "ReleaseControllerPowerSupply"}, - {7, &PSM::OpenSession, "OpenSession"}, - {8, nullptr, "EnableEnoughPowerChargeEmulation"}, - {9, nullptr, "DisableEnoughPowerChargeEmulation"}, - {10, nullptr, "EnableFastBatteryCharging"}, - {11, nullptr, "DisableFastBatteryCharging"}, - {12, nullptr, "GetBatteryVoltageState"}, - {13, nullptr, "GetRawBatteryChargePercentage"}, - {14, nullptr, "IsEnoughPowerSupplied"}, - {15, nullptr, "GetBatteryAgePercentage"}, - {16, nullptr, "GetBatteryChargeInfoEvent"}, - {17, nullptr, "GetBatteryChargeInfoFields"}, - {18, nullptr, "GetBatteryChargeCalibratedEvent"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - - ~PSM() override = default; - -private: - void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PSM, "called"); +PSM::PSM(Core::System& system_) : ServiceFramework{system_, "psm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"}, + {1, &PSM::GetChargerType, "GetChargerType"}, + {2, nullptr, "EnableBatteryCharging"}, + {3, nullptr, "DisableBatteryCharging"}, + {4, nullptr, "IsBatteryChargingEnabled"}, + {5, nullptr, "AcquireControllerPowerSupply"}, + {6, nullptr, "ReleaseControllerPowerSupply"}, + {7, &PSM::OpenSession, "OpenSession"}, + {8, nullptr, "EnableEnoughPowerChargeEmulation"}, + {9, nullptr, "DisableEnoughPowerChargeEmulation"}, + {10, nullptr, "EnableFastBatteryCharging"}, + {11, nullptr, "DisableFastBatteryCharging"}, + {12, nullptr, "GetBatteryVoltageState"}, + {13, nullptr, "GetRawBatteryChargePercentage"}, + {14, nullptr, "IsEnoughPowerSupplied"}, + {15, nullptr, "GetBatteryAgePercentage"}, + {16, nullptr, "GetBatteryChargeInfoEvent"}, + {17, nullptr, "GetBatteryChargeInfoFields"}, + {18, nullptr, "GetBatteryChargeCalibratedEvent"}, + }; + // clang-format on - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(battery_charge_percentage); - } + RegisterHandlers(functions); +} - void GetChargerType(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PSM, "called"); +PSM::~PSM() = default; - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(charger_type); - } +void PSM::GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PTM, "called"); - void OpenSession(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PSM, "called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(battery_charge_percentage); +} - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IPsmSession>(system); - } +void PSM::GetChargerType(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PTM, "called"); - enum class ChargerType : u32 { - Unplugged = 0, - RegularCharger = 1, - LowPowerCharger = 2, - Unknown = 3, - }; + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(charger_type); +} - u32 battery_charge_percentage{100}; // 100% - ChargerType charger_type{ChargerType::RegularCharger}; -}; +void PSM::OpenSession(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PTM, "called"); -void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { - std::make_shared<PSM>(system)->InstallAsService(sm); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IPsmSession>(system); } -} // namespace Service::PSM +} // namespace Service::PTM diff --git a/src/core/hle/service/ptm/psm.h b/src/core/hle/service/ptm/psm.h index 2930ce26a..f674ba8bc 100644 --- a/src/core/hle/service/ptm/psm.h +++ b/src/core/hle/service/ptm/psm.h @@ -1,19 +1,31 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -namespace Core { -class System; -} +#include "core/hle/service/service.h" -namespace Service::SM { -class ServiceManager; -} +namespace Service::PTM { -namespace Service::PSM { +class PSM final : public ServiceFramework<PSM> { +public: + explicit PSM(Core::System& system_); + ~PSM() override; -void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); +private: + enum class ChargerType : u32 { + Unplugged = 0, + RegularCharger = 1, + LowPowerCharger = 2, + Unknown = 3, + }; -} // namespace Service::PSM + void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx); + void GetChargerType(Kernel::HLERequestContext& ctx); + void OpenSession(Kernel::HLERequestContext& ctx); + + u32 battery_charge_percentage{100}; + ChargerType charger_type{ChargerType::RegularCharger}; +}; + +} // namespace Service::PTM diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp new file mode 100644 index 000000000..4bea995c6 --- /dev/null +++ b/src/core/hle/service/ptm/ptm.cpp @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <memory> + +#include "core/core.h" +#include "core/hle/service/ptm/psm.h" +#include "core/hle/service/ptm/ptm.h" +#include "core/hle/service/ptm/ts.h" + +namespace Service::PTM { + +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { + std::make_shared<PSM>(system)->InstallAsService(sm); + std::make_shared<TS>(system)->InstallAsService(sm); +} + +} // namespace Service::PTM diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h new file mode 100644 index 000000000..06224a24e --- /dev/null +++ b/src/core/hle/service/ptm/ptm.h @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +namespace Core { +class System; +} + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::PTM { + +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); + +} // namespace Service::PTM diff --git a/src/core/hle/service/ptm/ts.cpp b/src/core/hle/service/ptm/ts.cpp new file mode 100644 index 000000000..65c3f135f --- /dev/null +++ b/src/core/hle/service/ptm/ts.cpp @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <memory> + +#include "core/core.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/ptm/ts.h" + +namespace Service::PTM { + +TS::TS(Core::System& system_) : ServiceFramework{system_, "ts"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetTemperatureRange"}, + {1, &TS::GetTemperature, "GetTemperature"}, + {2, nullptr, "SetMeasurementMode"}, + {3, nullptr, "GetTemperatureMilliC"}, + {4, nullptr, "OpenSession"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +TS::~TS() = default; + +void TS::GetTemperature(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto location{rp.PopEnum<Location>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called. location={}", location); + + const s32 temperature = location == Location::Internal ? 35 : 20; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(temperature); +} + +} // namespace Service::PTM diff --git a/src/core/hle/service/ptm/ts.h b/src/core/hle/service/ptm/ts.h new file mode 100644 index 000000000..39a734ef7 --- /dev/null +++ b/src/core/hle/service/ptm/ts.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/service/service.h" + +namespace Service::PTM { + +class TS final : public ServiceFramework<TS> { +public: + explicit TS(Core::System& system_); + ~TS() override; + +private: + enum class Location : u8 { + Internal, + External, + }; + + void GetTemperature(Kernel::HLERequestContext& ctx); +}; + +} // namespace Service::PTM diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index f54e6fe56..dadaf897f 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <fmt/format.h> #include "common/assert.h" @@ -32,6 +31,7 @@ #include "core/hle/service/glue/glue.h" #include "core/hle/service/grc/grc.h" #include "core/hle/service/hid/hid.h" +#include "core/hle/service/jit/jit.h" #include "core/hle/service/lbl/lbl.h" #include "core/hle/service/ldn/ldn.h" #include "core/hle/service/ldr/ldr.h" @@ -39,6 +39,7 @@ #include "core/hle/service/mig/mig.h" #include "core/hle/service/mii/mii.h" #include "core/hle/service/mm/mm_u.h" +#include "core/hle/service/mnpp/mnpp_app.h" #include "core/hle/service/ncm/ncm.h" #include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nfp/nfp.h" @@ -48,6 +49,7 @@ #include "core/hle/service/npns/npns.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvflinger/hos_binder_driver_server.h" #include "core/hle/service/nvflinger/nvflinger.h" #include "core/hle/service/olsc/olsc.h" #include "core/hle/service/pcie/pcie.h" @@ -56,7 +58,7 @@ #include "core/hle/service/pm/pm.h" #include "core/hle/service/prepo/prepo.h" #include "core/hle/service/psc/psc.h" -#include "core/hle/service/ptm/psm.h" +#include "core/hle/service/ptm/ptm.h" #include "core/hle/service/service.h" #include "core/hle/service/set/settings.h" #include "core/hle/service/sm/sm.h" @@ -89,8 +91,9 @@ namespace Service { } ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_, - u32 max_sessions_, InvokerFn* handler_invoker_) - : SessionRequestHandler(system_.Kernel(), service_name_), system{system_}, + ServiceThreadType thread_type, u32 max_sessions_, + InvokerFn* handler_invoker_) + : SessionRequestHandler(system_.Kernel(), service_name_, thread_type), system{system_}, service_name{service_name_}, max_sessions{max_sessions_}, handler_invoker{handler_invoker_} {} ServiceFrameworkBase::~ServiceFrameworkBase() { @@ -187,17 +190,20 @@ void ServiceFrameworkBase::InvokeRequestTipc(Kernel::HLERequestContext& ctx) { handler_invoker(this, info->handler_callback, ctx); } -ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session, - Kernel::HLERequestContext& ctx) { +Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session, + Kernel::HLERequestContext& ctx) { const auto guard = LockService(); + Result result = ResultSuccess; + switch (ctx.GetCommandType()) { case IPC::CommandType::Close: case IPC::CommandType::TIPC_Close: { session.Close(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); - return IPC::ERR_REMOTE_PROCESS_DEAD; + result = IPC::ERR_REMOTE_PROCESS_DEAD; + break; } case IPC::CommandType::ControlWithContext: case IPC::CommandType::Control: { @@ -224,12 +230,13 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& sessi ctx.WriteToOutgoingCommandBuffer(ctx.GetThread()); } - return ResultSuccess; + return result; } /// Initialize Services Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) - : nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system)} { + : hos_binder_driver_server{std::make_unique<NVFlinger::HosBinderDriverServer>(system)}, + nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system, *hos_binder_driver_server)} { // 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. @@ -258,6 +265,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system Glue::InstallInterfaces(system); GRC::InstallInterfaces(*sm, system); HID::InstallInterfaces(*sm, system); + JIT::InstallInterfaces(*sm, system); LBL::InstallInterfaces(*sm, system); LDN::InstallInterfaces(*sm, system); LDR::InstallInterfaces(*sm, system); @@ -265,6 +273,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system Migration::InstallInterfaces(*sm, system); Mii::InstallInterfaces(*sm, system); MM::InstallInterfaces(*sm, system); + MNPP::InstallInterfaces(*sm, system); NCM::InstallInterfaces(*sm, system); NFC::InstallInterfaces(*sm, system); NFP::InstallInterfaces(*sm, system); @@ -281,14 +290,14 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system PlayReport::InstallInterfaces(*sm, system); PM::InstallInterfaces(system); PSC::InstallInterfaces(*sm, system); - PSM::InstallInterfaces(*sm, system); + PTM::InstallInterfaces(*sm, system); Set::InstallInterfaces(*sm, system); Sockets::InstallInterfaces(*sm, system); SPL::InstallInterfaces(*sm, system); SSL::InstallInterfaces(*sm, system); Time::InstallInterfaces(system); USB::InstallInterfaces(*sm, system); - VI::InstallInterfaces(*sm, system, *nv_flinger); + VI::InstallInterfaces(*sm, system, *nv_flinger, *hos_binder_driver_server); WLAN::InstallInterfaces(*sm, system); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index c9d6b879d..5bf197c51 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -9,7 +8,6 @@ #include <string> #include <boost/container/flat_map.hpp> #include "common/common_types.h" -#include "common/spin_lock.h" #include "core/hle/kernel/hle_ipc.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -33,8 +31,9 @@ class FileSystemController; } namespace NVFlinger { +class HosBinderDriverServer; class NVFlinger; -} +} // namespace NVFlinger namespace SM { class ServiceManager; @@ -80,8 +79,8 @@ public: Kernel::KClientPort& CreatePort(); /// Handles a synchronization request for the service. - ResultCode HandleSyncRequest(Kernel::KServerSession& session, - Kernel::HLERequestContext& context) override; + Result HandleSyncRequest(Kernel::KServerSession& session, + Kernel::HLERequestContext& context) override; protected: /// Member-function pointer type of SyncRequest handlers. @@ -89,7 +88,7 @@ protected: using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&); /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. - [[nodiscard]] std::scoped_lock<Common::SpinLock> LockService() { + [[nodiscard]] std::scoped_lock<std::mutex> LockService() { return std::scoped_lock{lock_service}; } @@ -113,7 +112,8 @@ private: Kernel::HLERequestContext& ctx); explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_, - u32 max_sessions_, InvokerFn* handler_invoker_); + ServiceThreadType thread_type, u32 max_sessions_, + InvokerFn* handler_invoker_); ~ServiceFrameworkBase() override; void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n); @@ -133,7 +133,7 @@ private: boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc; /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. - Common::SpinLock lock_service; + std::mutex lock_service; }; /** @@ -175,14 +175,17 @@ protected: /** * Initializes the handler with no functions installed. * - * @param system_ The system context to construct this service under. + * @param system_ The system context to construct this service under. * @param service_name_ Name of the service. - * @param max_sessions_ Maximum number of sessions that can be - * connected to this service at the same time. + * @param thread_type Specifies the thread type for this service. If this is set to CreateNew, + * it creates a new thread for it, otherwise this uses the default thread. + * @param max_sessions_ Maximum number of sessions that can be connected to this service at the + * same time. */ explicit ServiceFramework(Core::System& system_, const char* service_name_, + ServiceThreadType thread_type = ServiceThreadType::Default, u32 max_sessions_ = ServerSessionCountMax) - : ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {} + : ServiceFrameworkBase(system_, service_name_, thread_type, max_sessions_, Invoker) {} /// Registers handlers in the service. template <std::size_t N> @@ -236,6 +239,7 @@ public: ~Services(); private: + std::unique_ptr<NVFlinger::HosBinderDriverServer> hos_binder_driver_server; std::unique_ptr<NVFlinger::NVFlinger> nv_flinger; }; diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 8795eb6b7..f761c2da4 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> @@ -75,7 +74,7 @@ constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> language_to_la constexpr std::size_t PRE_4_0_0_MAX_ENTRIES = 0xF; constexpr std::size_t POST_4_0_0_MAX_ENTRIES = 0x40; -constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625}; +constexpr Result ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625}; void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_language_codes) { IPC::ResponseBuilder rb{ctx, 3}; diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index acabebeaa..60cad3e6f 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp index b2aa7bc0c..d2c0d536f 100644 --- a/src/core/hle/service/set/set_cal.cpp +++ b/src/core/hle/service/set/set_cal.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/set/set_cal.h" diff --git a/src/core/hle/service/set/set_cal.h b/src/core/hle/service/set/set_cal.h index a29fc3ddd..8f50278ed 100644 --- a/src/core/hle/service/set/set_cal.h +++ b/src/core/hle/service/set/set_cal.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/set/set_fd.cpp b/src/core/hle/service/set/set_fd.cpp index f04dc5047..278ef32e1 100644 --- a/src/core/hle/service/set/set_fd.cpp +++ b/src/core/hle/service/set/set_fd.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/set/set_fd.h" diff --git a/src/core/hle/service/set/set_fd.h b/src/core/hle/service/set/set_fd.h index c28cb301e..150a7cbce 100644 --- a/src/core/hle/service/set/set_fd.h +++ b/src/core/hle/service/set/set_fd.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index 38e6eae04..2a0b812c1 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/logging/log.h" @@ -33,7 +32,7 @@ void GetFirmwareVersionImpl(Kernel::HLERequestContext& ctx, GetFirmwareVersionTy // consistence (currently reports as 5.1.0-0.0) const auto archive = FileSys::SystemArchive::SystemVersion(); - const auto early_exit_failure = [&ctx](std::string_view desc, ResultCode code) { + const auto early_exit_failure = [&ctx](std::string_view desc, Result code) { LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).", desc); IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h index edb185a68..ac97772b7 100644 --- a/src/core/hle/service/set/set_sys.h +++ b/src/core/hle/service/set/set_sys.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/set/settings.cpp b/src/core/hle/service/set/settings.cpp index 212ebc427..4ebc2a0ec 100644 --- a/src/core/hle/service/set/settings.cpp +++ b/src/core/hle/service/set/settings.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/set/set.h" #include "core/hle/service/set/set_cal.h" diff --git a/src/core/hle/service/set/settings.h b/src/core/hle/service/set/settings.h index 7a6950dd0..6cd7d634c 100644 --- a/src/core/hle/service/set/settings.h +++ b/src/core/hle/service/set/settings.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index eaa172595..246c94623 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <tuple> #include "common/assert.h" @@ -18,10 +17,10 @@ namespace Service::SM { -constexpr ResultCode ERR_NOT_INITIALIZED(ErrorModule::SM, 2); -constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::SM, 4); -constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6); -constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); +constexpr Result ERR_NOT_INITIALIZED(ErrorModule::SM, 2); +constexpr Result ERR_ALREADY_REGISTERED(ErrorModule::SM, 4); +constexpr Result ERR_INVALID_NAME(ErrorModule::SM, 6); +constexpr Result ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {} ServiceManager::~ServiceManager() = default; @@ -30,7 +29,7 @@ void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { controller_interface->InvokeRequest(context); } -static ResultCode ValidateServiceName(const std::string& name) { +static Result ValidateServiceName(const std::string& name) { if (name.empty() || name.size() > 8) { LOG_ERROR(Service_SM, "Invalid service name! service={}", name); return ERR_INVALID_NAME; @@ -44,8 +43,8 @@ Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core return self.sm_interface->CreatePort(); } -ResultCode ServiceManager::RegisterService(std::string name, u32 max_sessions, - Kernel::SessionRequestHandlerPtr handler) { +Result ServiceManager::RegisterService(std::string name, u32 max_sessions, + Kernel::SessionRequestHandlerPtr handler) { CASCADE_CODE(ValidateServiceName(name)); @@ -59,7 +58,7 @@ ResultCode ServiceManager::RegisterService(std::string name, u32 max_sessions, return ResultSuccess; } -ResultCode ServiceManager::UnregisterService(const std::string& name) { +Result ServiceManager::UnregisterService(const std::string& name) { CASCADE_CODE(ValidateServiceName(name)); const auto iter = registered_services.find(name); @@ -81,6 +80,8 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name } auto* port = Kernel::KPort::Create(kernel); + SCOPE_EXIT({ port->Close(); }); + port->Initialize(ServerSessionCountMax, false, name); auto handler = it->second; port->GetServerPort().SetSessionHandler(std::move(handler)); @@ -93,7 +94,7 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name * Inputs: * 0: 0x00000000 * Outputs: - * 0: ResultCode + * 0: Result */ void SM::Initialize(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_SM, "called"); @@ -151,7 +152,7 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext& auto& port = port_result.Unwrap(); SCOPE_EXIT({ port->GetClientPort().Close(); }); - server_ports.emplace_back(&port->GetServerPort()); + kernel.RegisterServerObject(&port->GetServerPort()); // Create a new session. Kernel::KClientSession* session{}; @@ -204,7 +205,7 @@ void SM::UnregisterService(Kernel::HLERequestContext& ctx) { } SM::SM(ServiceManager& service_manager_, Core::System& system_) - : ServiceFramework{system_, "sm:", 4}, + : ServiceFramework{system_, "sm:", ServiceThreadType::Default, 4}, service_manager{service_manager_}, kernel{system_.Kernel()} { RegisterHandlers({ {0, &SM::Initialize, "Initialize"}, @@ -222,10 +223,6 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_) }); } -SM::~SM() { - for (auto& server_port : server_ports) { - server_port->Close(); - } -} +SM::~SM() = default; } // namespace Service::SM diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 021eb51b4..878decc6f 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -22,7 +21,6 @@ class KClientPort; class KClientSession; class KernelCore; class KPort; -class KServerPort; class SessionRequestHandler; } // namespace Kernel @@ -48,7 +46,6 @@ private: ServiceManager& service_manager; bool is_initialized{}; Kernel::KernelCore& kernel; - std::vector<Kernel::KServerPort*> server_ports; }; class ServiceManager { @@ -58,9 +55,9 @@ public: explicit ServiceManager(Kernel::KernelCore& kernel_); ~ServiceManager(); - ResultCode RegisterService(std::string name, u32 max_sessions, - Kernel::SessionRequestHandlerPtr handler); - ResultCode UnregisterService(const std::string& name); + Result RegisterService(std::string name, u32 max_sessions, + Kernel::SessionRequestHandlerPtr handler); + Result UnregisterService(const std::string& name); ResultVal<Kernel::KPort*> GetServicePort(const std::string& name); template <Common::DerivedFrom<Kernel::SessionRequestHandler> T> diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp index 09f9ecee1..2a4bd64ab 100644 --- a/src/core/hle/service/sm/sm_controller.cpp +++ b/src/core/hle/service/sm/sm_controller.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/logging/log.h" @@ -34,7 +33,7 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { // Create a session. Kernel::KClientSession* session{}; - const ResultCode result = parent_port.CreateSession(std::addressof(session), session_manager); + const Result result = parent_port.CreateSession(std::addressof(session), session_manager); if (result.IsError()) { LOG_CRITICAL(Service, "CreateSession failed with error 0x{:08X}", result.raw); IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/core/hle/service/sm/sm_controller.h b/src/core/hle/service/sm/sm_controller.h index 7494f898d..ed386f660 100644 --- a/src/core/hle/service/sm/sm_controller.h +++ b/src/core/hle/service/sm/sm_controller.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index f83272633..9e94a462f 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include <memory> @@ -10,13 +9,16 @@ #include <fmt/format.h> #include "common/microprofile.h" -#include "common/thread.h" +#include "common/socket_types.h" +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/service/sockets/bsd.h" #include "core/hle/service/sockets/sockets_translate.h" -#include "core/network/network.h" -#include "core/network/sockets.h" +#include "core/internal_network/network.h" +#include "core/internal_network/socket_proxy.h" +#include "core/internal_network/sockets.h" +#include "network/network.h" namespace Service::Sockets { @@ -474,7 +476,13 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco LOG_INFO(Service, "New socket fd={}", fd); - descriptor.socket = std::make_unique<Network::Socket>(); + auto room_member = room_network.GetRoomMember().lock(); + if (room_member && room_member->IsConnected()) { + descriptor.socket = std::make_unique<Network::ProxySocket>(room_network); + } else { + descriptor.socket = std::make_unique<Network::Socket>(); + } + descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol)); descriptor.is_connection_based = IsConnectionBased(type); @@ -569,9 +577,9 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) { new_descriptor.socket = std::move(result.socket); new_descriptor.is_connection_based = descriptor.is_connection_based; - ASSERT(write_buffer.size() == sizeof(SockAddrIn)); const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); - std::memcpy(write_buffer.data(), &guest_addr_in, sizeof(guest_addr_in)); + const size_t length = std::min(sizeof(guest_addr_in), write_buffer.size()); + std::memcpy(write_buffer.data(), &guest_addr_in, length); return {new_fd, Errno::SUCCESS}; } @@ -650,7 +658,7 @@ std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) { ASSERT(arg == 0); return {descriptor.flags, Errno::SUCCESS}; case FcntlCmd::SETFL: { - const bool enable = (arg & FLAG_O_NONBLOCK) != 0; + const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0; const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable)); if (bsd_errno != Errno::SUCCESS) { return {-1, bsd_errno}; @@ -671,7 +679,7 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con return Errno::BADF; } - Network::Socket* const socket = file_descriptors[fd]->socket.get(); + Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); if (optname == OptName::LINGER) { ASSERT(optlen == sizeof(Linger)); @@ -690,6 +698,9 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con case OptName::REUSEADDR: ASSERT(value == 0 || value == 1); return Translate(socket->SetReuseAddr(value != 0)); + case OptName::KEEPALIVE: + ASSERT(value == 0 || value == 1); + return Translate(socket->SetKeepAlive(value != 0)); case OptName::BROADCAST: ASSERT(value == 0 || value == 1); return Translate(socket->SetBroadcast(value != 0)); @@ -719,7 +730,27 @@ std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) if (!IsFileDescriptorValid(fd)) { return {-1, Errno::BADF}; } - return Translate(file_descriptors[fd]->socket->Recv(flags, message)); + + FileDescriptor& descriptor = *file_descriptors[fd]; + + // Apply flags + using Network::FLAG_MSG_DONTWAIT; + using Network::FLAG_O_NONBLOCK; + if ((flags & FLAG_MSG_DONTWAIT) != 0) { + flags &= ~FLAG_MSG_DONTWAIT; + if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { + descriptor.socket->SetNonBlock(true); + } + } + + const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message)); + + // Restore original state + if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { + descriptor.socket->SetNonBlock(false); + } + + return {ret, bsd_errno}; } std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, @@ -740,6 +771,8 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess } // Apply flags + using Network::FLAG_MSG_DONTWAIT; + using Network::FLAG_O_NONBLOCK; if ((flags & FLAG_MSG_DONTWAIT) != 0) { flags &= ~FLAG_MSG_DONTWAIT; if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { @@ -838,7 +871,19 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co rb.PushEnum(bsd_errno); } -BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { +void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) { + for (auto& optional_descriptor : file_descriptors) { + if (!optional_descriptor.has_value()) { + continue; + } + FileDescriptor& descriptor = *optional_descriptor; + descriptor.socket.get()->HandleProxyPacket(packet); + } +} + +BSD::BSD(Core::System& system_, const char* name) + : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, room_network{ + system_.GetRoomNetwork()} { // clang-format off static const FunctionInfo functions[] = { {0, &BSD::RegisterClient, "RegisterClient"}, @@ -879,9 +924,20 @@ BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, na // clang-format on RegisterHandlers(functions); + + if (auto room_member = room_network.GetRoomMember().lock()) { + proxy_packet_received = room_member->BindOnProxyPacketReceived( + [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); }); + } else { + LOG_ERROR(Service, "Network isn't initialized"); + } } -BSD::~BSD() = default; +BSD::~BSD() { + if (auto room_member = room_network.GetRoomMember().lock()) { + room_member->Unbind(proxy_packet_received); + } +} BSDCFG::BSDCFG(Core::System& system_) : ServiceFramework{system_, "bsdcfg"} { // clang-format off diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index a387e50df..81e855e0f 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,16 +7,19 @@ #include <vector> #include "common/common_types.h" +#include "common/socket_types.h" #include "core/hle/service/service.h" #include "core/hle/service/sockets/sockets.h" +#include "network/network.h" namespace Core { class System; } namespace Network { +class SocketBase; class Socket; -} +} // namespace Network namespace Service::Sockets { @@ -31,7 +33,7 @@ private: static constexpr size_t MAX_FD = 128; struct FileDescriptor { - std::unique_ptr<Network::Socket> socket; + std::unique_ptr<Network::SocketBase> socket; s32 flags = 0; bool is_connection_based = false; }; @@ -166,6 +168,14 @@ private: void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept; std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors; + + Network::RoomNetwork& room_network; + + /// Callback to parse and handle a received wifi packet. + void OnProxyPacketReceived(const Network::ProxyPacket& packet); + + // Callback identifier for the OnProxyPacketReceived event. + Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received; }; class BSDCFG final : public ServiceFramework<BSDCFG> { diff --git a/src/core/hle/service/sockets/ethc.cpp b/src/core/hle/service/sockets/ethc.cpp index 899a64c2f..c12ea999b 100644 --- a/src/core/hle/service/sockets/ethc.cpp +++ b/src/core/hle/service/sockets/ethc.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/sockets/ethc.h" diff --git a/src/core/hle/service/sockets/ethc.h b/src/core/hle/service/sockets/ethc.h index 71884182e..7c5759a96 100644 --- a/src/core/hle/service/sockets/ethc.h +++ b/src/core/hle/service/sockets/ethc.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/sockets/nsd.cpp b/src/core/hle/service/sockets/nsd.cpp index 1159debc5..6491a73be 100644 --- a/src/core/hle/service/sockets/nsd.cpp +++ b/src/core/hle/service/sockets/nsd.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/sockets/nsd.h" diff --git a/src/core/hle/service/sockets/nsd.h b/src/core/hle/service/sockets/nsd.h index becf93125..5cc12b855 100644 --- a/src/core/hle/service/sockets/nsd.h +++ b/src/core/hle/service/sockets/nsd.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp index fb6142c49..097c37d7a 100644 --- a/src/core/hle/service/sockets/sfdnsres.cpp +++ b/src/core/hle/service/sockets/sfdnsres.cpp @@ -1,9 +1,28 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#include <string_view> +#include <utility> +#include <vector> + +#include "common/string_util.h" +#include "common/swap.h" +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/sockets/sfdnsres.h" +#include "core/memory.h" + +#ifdef _WIN32 +#include <ws2tcpip.h> +#elif YUZU_UNIX +#include <arpa/inet.h> +#include <netdb.h> +#include <netinet/in.h> +#include <sys/socket.h> +#ifndef EAI_NODATA +#define EAI_NODATA EAI_NONAME +#endif +#endif namespace Service::Sockets { @@ -21,7 +40,7 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres" {9, nullptr, "CancelRequest"}, {10, nullptr, "GetHostByNameRequestWithOptions"}, {11, nullptr, "GetHostByAddrRequestWithOptions"}, - {12, nullptr, "GetAddrInfoRequestWithOptions"}, + {12, &SFDNSRES::GetAddrInfoRequestWithOptions, "GetAddrInfoRequestWithOptions"}, {13, nullptr, "GetNameInfoRequestWithOptions"}, {14, nullptr, "ResolverSetOptionRequest"}, {15, nullptr, "ResolverGetOptionRequest"}, @@ -31,7 +50,142 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres" SFDNSRES::~SFDNSRES() = default; -void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) { +enum class NetDbError : s32 { + Internal = -1, + Success = 0, + HostNotFound = 1, + TryAgain = 2, + NoRecovery = 3, + NoData = 4, +}; + +static NetDbError AddrInfoErrorToNetDbError(s32 result) { + // Best effort guess to map errors + switch (result) { + case 0: + return NetDbError::Success; + case EAI_AGAIN: + return NetDbError::TryAgain; + case EAI_NODATA: + return NetDbError::NoData; + default: + return NetDbError::HostNotFound; + } +} + +static std::vector<u8> SerializeAddrInfo(const addrinfo* addrinfo, s32 result_code, + std::string_view host) { + // Adapted from + // https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190 + std::vector<u8> data; + + auto* current = addrinfo; + while (current != nullptr) { + struct SerializedResponseHeader { + u32 magic; + s32 flags; + s32 family; + s32 socket_type; + s32 protocol; + u32 address_length; + }; + static_assert(sizeof(SerializedResponseHeader) == 0x18, + "Response header size must be 0x18 bytes"); + + constexpr auto header_size = sizeof(SerializedResponseHeader); + const auto addr_size = + current->ai_addr && current->ai_addrlen > 0 ? current->ai_addrlen : 4; + const auto canonname_size = current->ai_canonname ? strlen(current->ai_canonname) + 1 : 1; + + const auto last_size = data.size(); + data.resize(last_size + header_size + addr_size + canonname_size); + + // Header in network byte order + SerializedResponseHeader header{}; + + constexpr auto HEADER_MAGIC = 0xBEEFCAFE; + header.magic = htonl(HEADER_MAGIC); + header.family = htonl(current->ai_family); + header.flags = htonl(current->ai_flags); + header.socket_type = htonl(current->ai_socktype); + header.protocol = htonl(current->ai_protocol); + header.address_length = current->ai_addr ? htonl((u32)current->ai_addrlen) : 0; + + auto* header_ptr = data.data() + last_size; + std::memcpy(header_ptr, &header, header_size); + + if (header.address_length == 0) { + std::memset(header_ptr + header_size, 0, 4); + } else { + switch (current->ai_family) { + case AF_INET: { + struct SockAddrIn { + s16 sin_family; + u16 sin_port; + u32 sin_addr; + u8 sin_zero[8]; + }; + + SockAddrIn serialized_addr{}; + const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr); + serialized_addr.sin_port = htons(addr.sin_port); + serialized_addr.sin_family = htons(addr.sin_family); + serialized_addr.sin_addr = htonl(addr.sin_addr.s_addr); + std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn)); + + char addr_string_buf[64]{}; + inet_ntop(AF_INET, &addr.sin_addr, addr_string_buf, std::size(addr_string_buf)); + LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, addr_string_buf); + break; + } + case AF_INET6: { + struct SockAddrIn6 { + s16 sin6_family; + u16 sin6_port; + u32 sin6_flowinfo; + u8 sin6_addr[16]; + u32 sin6_scope_id; + }; + + SockAddrIn6 serialized_addr{}; + const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr); + serialized_addr.sin6_family = htons(addr.sin6_family); + serialized_addr.sin6_port = htons(addr.sin6_port); + serialized_addr.sin6_flowinfo = htonl(addr.sin6_flowinfo); + serialized_addr.sin6_scope_id = htonl(addr.sin6_scope_id); + std::memcpy(serialized_addr.sin6_addr, &addr.sin6_addr, + sizeof(SockAddrIn6::sin6_addr)); + std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn6)); + + char addr_string_buf[64]{}; + inet_ntop(AF_INET6, &addr.sin6_addr, addr_string_buf, std::size(addr_string_buf)); + LOG_INFO(Service, "Resolved host '{}' to IPv6 address {}", host, addr_string_buf); + break; + } + default: + std::memcpy(header_ptr + header_size, current->ai_addr, addr_size); + break; + } + } + if (current->ai_canonname) { + std::memcpy(header_ptr + addr_size, current->ai_canonname, canonname_size); + } else { + *(header_ptr + header_size + addr_size) = 0; + } + + current = current->ai_next; + } + + // 4-byte sentinel value + data.push_back(0); + data.push_back(0); + data.push_back(0); + data.push_back(0); + + return data; +} + +static std::pair<u32, s32> GetAddrInfoRequestImpl(Kernel::HLERequestContext& ctx) { struct Parameters { u8 use_nsd_resolve; u32 unknown; @@ -42,11 +196,51 @@ void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) { const auto parameters = rp.PopRaw<Parameters>(); LOG_WARNING(Service, - "(STUBBED) called. use_nsd_resolve={}, unknown=0x{:08X}, process_id=0x{:016X}", + "called with ignored parameters: use_nsd_resolve={}, unknown={}, process_id={}", parameters.use_nsd_resolve, parameters.unknown, parameters.process_id); - IPC::ResponseBuilder rb{ctx, 2}; + const auto host_buffer = ctx.ReadBuffer(0); + const std::string host = Common::StringFromBuffer(host_buffer); + + const auto service_buffer = ctx.ReadBuffer(1); + const std::string service = Common::StringFromBuffer(service_buffer); + + addrinfo* addrinfo; + // Pass null for hints. Serialized hints are also passed in a buffer, but are ignored for now + s32 result_code = getaddrinfo(host.c_str(), service.c_str(), nullptr, &addrinfo); + + u32 data_size = 0; + if (result_code == 0 && addrinfo != nullptr) { + const std::vector<u8>& data = SerializeAddrInfo(addrinfo, result_code, host); + data_size = static_cast<u32>(data.size()); + freeaddrinfo(addrinfo); + + ctx.WriteBuffer(data, 0); + } + + return std::make_pair(data_size, result_code); +} + +void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) { + auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode + rb.Push(result_code); // errno + rb.Push(data_size); // serialized size +} + +void SFDNSRES::GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx) { + // Additional options are ignored + auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx); + + IPC::ResponseBuilder rb{ctx, 5}; rb.Push(ResultSuccess); + rb.Push(data_size); // serialized size + rb.Push(result_code); // errno + rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode + rb.Push(0); } -} // namespace Service::Sockets +} // namespace Service::Sockets
\ No newline at end of file diff --git a/src/core/hle/service/sockets/sfdnsres.h b/src/core/hle/service/sockets/sfdnsres.h index 5d3b4dc2d..96018ea77 100644 --- a/src/core/hle/service/sockets/sfdnsres.h +++ b/src/core/hle/service/sockets/sfdnsres.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -19,6 +18,7 @@ public: private: void GetAddrInfoRequest(Kernel::HLERequestContext& ctx); + void GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx); }; } // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/sockets.cpp b/src/core/hle/service/sockets/sockets.cpp index 96f73bce3..8d3ba6f96 100644 --- a/src/core/hle/service/sockets/sockets.cpp +++ b/src/core/hle/service/sockets/sockets.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/sockets/bsd.h" #include "core/hle/service/sockets/ethc.h" diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h index 02dbbae40..31b7dad33 100644 --- a/src/core/hle/service/sockets/sockets.h +++ b/src/core/hle/service/sockets/sockets.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -23,7 +22,9 @@ enum class Errno : u32 { AGAIN = 11, INVAL = 22, MFILE = 24, + MSGSIZE = 90, NOTCONN = 107, + TIMEDOUT = 110, }; enum class Domain : u32 { @@ -46,6 +47,7 @@ enum class Protocol : u32 { enum class OptName : u32 { REUSEADDR = 0x4, + KEEPALIVE = 0x8, BROADCAST = 0x20, LINGER = 0x80, SNDBUF = 0x1001, @@ -96,10 +98,6 @@ struct Linger { u32 linger; }; -constexpr u32 FLAG_MSG_DONTWAIT = 0x80; - -constexpr u32 FLAG_O_NONBLOCK = 0x800; - /// Registers all Sockets services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp index ca61d72ca..023aa0486 100644 --- a/src/core/hle/service/sockets/sockets_translate.cpp +++ b/src/core/hle/service/sockets/sockets_translate.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <utility> @@ -8,7 +7,7 @@ #include "common/common_types.h" #include "core/hle/service/sockets/sockets.h" #include "core/hle/service/sockets/sockets_translate.h" -#include "core/network/network.h" +#include "core/internal_network/network.h" namespace Service::Sockets { @@ -26,6 +25,8 @@ Errno Translate(Network::Errno value) { return Errno::MFILE; case Network::Errno::NOTCONN: return Errno::NOTCONN; + case Network::Errno::TIMEDOUT: + return Errno::TIMEDOUT; default: UNIMPLEMENTED_MSG("Unimplemented errno={}", value); return Errno::SUCCESS; diff --git a/src/core/hle/service/sockets/sockets_translate.h b/src/core/hle/service/sockets/sockets_translate.h index 057d1ff22..c93291d3e 100644 --- a/src/core/hle/service/sockets/sockets_translate.h +++ b/src/core/hle/service/sockets/sockets_translate.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,7 +7,7 @@ #include "common/common_types.h" #include "core/hle/service/sockets/sockets.h" -#include "core/network/network.h" +#include "core/internal_network/network.h" namespace Service::Sockets { diff --git a/src/core/hle/service/spl/csrng.cpp b/src/core/hle/service/spl/csrng.cpp index 9c7f89475..ca121fb05 100644 --- a/src/core/hle/service/spl/csrng.cpp +++ b/src/core/hle/service/spl/csrng.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/spl/csrng.h" diff --git a/src/core/hle/service/spl/csrng.h b/src/core/hle/service/spl/csrng.h index 0d03cc6cb..b337a8281 100644 --- a/src/core/hle/service/spl/csrng.h +++ b/src/core/hle/service/spl/csrng.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/spl/spl.cpp b/src/core/hle/service/spl/spl.cpp index 20384042f..fde212186 100644 --- a/src/core/hle/service/spl/spl.cpp +++ b/src/core/hle/service/spl/spl.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/spl/spl.h" diff --git a/src/core/hle/service/spl/spl.h b/src/core/hle/service/spl/spl.h index 5599c0c01..7b63d8b1a 100644 --- a/src/core/hle/service/spl/spl.h +++ b/src/core/hle/service/spl/spl.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/spl/spl_module.cpp b/src/core/hle/service/spl/spl_module.cpp index 10f7d1461..64eae1ebf 100644 --- a/src/core/hle/service/spl/spl_module.cpp +++ b/src/core/hle/service/spl/spl_module.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstdlib> diff --git a/src/core/hle/service/spl/spl_module.h b/src/core/hle/service/spl/spl_module.h index 61630df80..4c9a3c618 100644 --- a/src/core/hle/service/spl/spl_module.h +++ b/src/core/hle/service/spl/spl_module.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/spl/spl_results.h b/src/core/hle/service/spl/spl_results.h index a07c61409..dd7ba11f3 100644 --- a/src/core/hle/service/spl/spl_results.h +++ b/src/core/hle/service/spl/spl_results.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -9,23 +8,23 @@ namespace Service::SPL { // Description 0 - 99 -constexpr ResultCode ResultSecureMonitorError{ErrorModule::SPL, 0}; -constexpr ResultCode ResultSecureMonitorNotImplemented{ErrorModule::SPL, 1}; -constexpr ResultCode ResultSecureMonitorInvalidArgument{ErrorModule::SPL, 2}; -constexpr ResultCode ResultSecureMonitorBusy{ErrorModule::SPL, 3}; -constexpr ResultCode ResultSecureMonitorNoAsyncOperation{ErrorModule::SPL, 4}; -constexpr ResultCode ResultSecureMonitorInvalidAsyncOperation{ErrorModule::SPL, 5}; -constexpr ResultCode ResultSecureMonitorNotPermitted{ErrorModule::SPL, 6}; -constexpr ResultCode ResultSecureMonitorNotInitialized{ErrorModule::SPL, 7}; +constexpr Result ResultSecureMonitorError{ErrorModule::SPL, 0}; +constexpr Result ResultSecureMonitorNotImplemented{ErrorModule::SPL, 1}; +constexpr Result ResultSecureMonitorInvalidArgument{ErrorModule::SPL, 2}; +constexpr Result ResultSecureMonitorBusy{ErrorModule::SPL, 3}; +constexpr Result ResultSecureMonitorNoAsyncOperation{ErrorModule::SPL, 4}; +constexpr Result ResultSecureMonitorInvalidAsyncOperation{ErrorModule::SPL, 5}; +constexpr Result ResultSecureMonitorNotPermitted{ErrorModule::SPL, 6}; +constexpr Result ResultSecureMonitorNotInitialized{ErrorModule::SPL, 7}; -constexpr ResultCode ResultInvalidSize{ErrorModule::SPL, 100}; -constexpr ResultCode ResultUnknownSecureMonitorError{ErrorModule::SPL, 101}; -constexpr ResultCode ResultDecryptionFailed{ErrorModule::SPL, 102}; +constexpr Result ResultInvalidSize{ErrorModule::SPL, 100}; +constexpr Result ResultUnknownSecureMonitorError{ErrorModule::SPL, 101}; +constexpr Result ResultDecryptionFailed{ErrorModule::SPL, 102}; -constexpr ResultCode ResultOutOfKeySlots{ErrorModule::SPL, 104}; -constexpr ResultCode ResultInvalidKeySlot{ErrorModule::SPL, 105}; -constexpr ResultCode ResultBootReasonAlreadySet{ErrorModule::SPL, 106}; -constexpr ResultCode ResultBootReasonNotSet{ErrorModule::SPL, 107}; -constexpr ResultCode ResultInvalidArgument{ErrorModule::SPL, 108}; +constexpr Result ResultOutOfKeySlots{ErrorModule::SPL, 104}; +constexpr Result ResultInvalidKeySlot{ErrorModule::SPL, 105}; +constexpr Result ResultBootReasonAlreadySet{ErrorModule::SPL, 106}; +constexpr Result ResultBootReasonNotSet{ErrorModule::SPL, 107}; +constexpr Result ResultInvalidArgument{ErrorModule::SPL, 108}; } // namespace Service::SPL diff --git a/src/core/hle/service/spl/spl_types.h b/src/core/hle/service/spl/spl_types.h index a654e7556..91f9cc032 100644 --- a/src/core/hle/service/spl/spl_types.h +++ b/src/core/hle/service/spl/spl_types.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp index a81a595ea..3735e0452 100644 --- a/src/core/hle/service/ssl/ssl.cpp +++ b/src/core/hle/service/ssl/ssl.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/ipc_helpers.h" #include "core/hle/service/service.h" diff --git a/src/core/hle/service/ssl/ssl.h b/src/core/hle/service/ssl/ssl.h index a3aa4b4b5..27b38a003 100644 --- a/src/core/hle/service/ssl/ssl.h +++ b/src/core/hle/service/ssl/ssl.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h index d0cacb80c..ef070f32f 100644 --- a/src/core/hle/service/time/clock_types.h +++ b/src/core/hle/service/time/clock_types.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -23,7 +22,7 @@ struct SteadyClockTimePoint { s64 time_point; Common::UUID clock_source_id; - ResultCode GetSpanBetween(SteadyClockTimePoint other, s64& span) const { + Result GetSpanBetween(SteadyClockTimePoint other, s64& span) const { span = 0; if (clock_source_id != other.clock_source_id) { @@ -93,9 +92,9 @@ struct ClockSnapshot { TimeType type; INSERT_PADDING_BYTES_NOINIT(0x2); - static ResultCode GetCurrentTime(s64& current_time, - const SteadyClockTimePoint& steady_clock_time_point, - const SystemClockContext& context) { + static Result GetCurrentTime(s64& current_time, + const SteadyClockTimePoint& steady_clock_time_point, + const SystemClockContext& context) { if (steady_clock_time_point.clock_source_id != context.steady_time_point.clock_source_id) { current_time = 0; return ERROR_TIME_MISMATCH; diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h b/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h index 42893e3f6..0f928a5a5 100644 --- a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h +++ b/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_core.h b/src/core/hle/service/time/ephemeral_network_system_clock_core.h index d12cb5335..0a5f5aafb 100644 --- a/src/core/hle/service/time/ephemeral_network_system_clock_core.h +++ b/src/core/hle/service/time/ephemeral_network_system_clock_core.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/errors.h b/src/core/hle/service/time/errors.h index 8501a3e8c..6655d30e1 100644 --- a/src/core/hle/service/time/errors.h +++ b/src/core/hle/service/time/errors.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,15 +7,15 @@ namespace Service::Time { -constexpr ResultCode ERROR_PERMISSION_DENIED{ErrorModule::Time, 1}; -constexpr ResultCode ERROR_TIME_MISMATCH{ErrorModule::Time, 102}; -constexpr ResultCode ERROR_UNINITIALIZED_CLOCK{ErrorModule::Time, 103}; -constexpr ResultCode ERROR_TIME_NOT_FOUND{ErrorModule::Time, 200}; -constexpr ResultCode ERROR_OVERFLOW{ErrorModule::Time, 201}; -constexpr ResultCode ERROR_LOCATION_NAME_TOO_LONG{ErrorModule::Time, 801}; -constexpr ResultCode ERROR_OUT_OF_RANGE{ErrorModule::Time, 902}; -constexpr ResultCode ERROR_TIME_ZONE_CONVERSION_FAILED{ErrorModule::Time, 903}; -constexpr ResultCode ERROR_TIME_ZONE_NOT_FOUND{ErrorModule::Time, 989}; -constexpr ResultCode ERROR_NOT_IMPLEMENTED{ErrorModule::Time, 990}; +constexpr Result ERROR_PERMISSION_DENIED{ErrorModule::Time, 1}; +constexpr Result ERROR_TIME_MISMATCH{ErrorModule::Time, 102}; +constexpr Result ERROR_UNINITIALIZED_CLOCK{ErrorModule::Time, 103}; +constexpr Result ERROR_TIME_NOT_FOUND{ErrorModule::Time, 200}; +constexpr Result ERROR_OVERFLOW{ErrorModule::Time, 201}; +constexpr Result ERROR_LOCATION_NAME_TOO_LONG{ErrorModule::Time, 801}; +constexpr Result ERROR_OUT_OF_RANGE{ErrorModule::Time, 902}; +constexpr Result ERROR_TIME_ZONE_CONVERSION_FAILED{ErrorModule::Time, 903}; +constexpr Result ERROR_TIME_ZONE_NOT_FOUND{ErrorModule::Time, 989}; +constexpr Result ERROR_NOT_IMPLEMENTED{ErrorModule::Time, 990}; } // namespace Service::Time diff --git a/src/core/hle/service/time/local_system_clock_context_writer.h b/src/core/hle/service/time/local_system_clock_context_writer.h index ac6c7b4b1..1639ef2b9 100644 --- a/src/core/hle/service/time/local_system_clock_context_writer.h +++ b/src/core/hle/service/time/local_system_clock_context_writer.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -15,7 +14,7 @@ public: : SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {} protected: - ResultCode Update() override { + Result Update() override { shared_memory.UpdateLocalSystemClockContext(context); return ResultSuccess; } diff --git a/src/core/hle/service/time/network_system_clock_context_writer.h b/src/core/hle/service/time/network_system_clock_context_writer.h index a54fd7fe1..655e4c06d 100644 --- a/src/core/hle/service/time/network_system_clock_context_writer.h +++ b/src/core/hle/service/time/network_system_clock_context_writer.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -16,7 +15,7 @@ public: : SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {} protected: - ResultCode Update() override { + Result Update() override { shared_memory.UpdateNetworkSystemClockContext(context); return ResultSuccess; } diff --git a/src/core/hle/service/time/standard_local_system_clock_core.h b/src/core/hle/service/time/standard_local_system_clock_core.h index 6320c7af1..ae2ff1bfd 100644 --- a/src/core/hle/service/time/standard_local_system_clock_core.h +++ b/src/core/hle/service/time/standard_local_system_clock_core.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/standard_network_system_clock_core.h b/src/core/hle/service/time/standard_network_system_clock_core.h index 95923a27b..c1ec5252b 100644 --- a/src/core/hle/service/time/standard_network_system_clock_core.h +++ b/src/core/hle/service/time/standard_network_system_clock_core.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp index a1ffdd524..3dbbb9850 100644 --- a/src/core/hle/service/time/standard_steady_clock_core.cpp +++ b/src/core/hle/service/time/standard_steady_clock_core.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/core_timing.h" diff --git a/src/core/hle/service/time/standard_steady_clock_core.h b/src/core/hle/service/time/standard_steady_clock_core.h index f56f3fd95..036463b87 100644 --- a/src/core/hle/service/time/standard_steady_clock_core.h +++ b/src/core/hle/service/time/standard_steady_clock_core.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/standard_user_system_clock_core.cpp b/src/core/hle/service/time/standard_user_system_clock_core.cpp index e94220a44..b033757ed 100644 --- a/src/core/hle/service/time/standard_user_system_clock_core.cpp +++ b/src/core/hle/service/time/standard_user_system_clock_core.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "core/core.h" @@ -28,9 +27,9 @@ StandardUserSystemClockCore::~StandardUserSystemClockCore() { service_context.CloseEvent(auto_correction_event); } -ResultCode StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system, - bool value) { - if (const ResultCode result{ApplyAutomaticCorrection(system, value)}; result != ResultSuccess) { +Result StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system, + bool value) { + if (const Result result{ApplyAutomaticCorrection(system, value)}; result != ResultSuccess) { return result; } @@ -39,27 +38,27 @@ ResultCode StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::Syst return ResultSuccess; } -ResultCode StandardUserSystemClockCore::GetClockContext(Core::System& system, - SystemClockContext& ctx) const { - if (const ResultCode result{ApplyAutomaticCorrection(system, false)}; result != ResultSuccess) { +Result StandardUserSystemClockCore::GetClockContext(Core::System& system, + SystemClockContext& ctx) const { + if (const Result result{ApplyAutomaticCorrection(system, false)}; result != ResultSuccess) { return result; } return local_system_clock_core.GetClockContext(system, ctx); } -ResultCode StandardUserSystemClockCore::Flush(const SystemClockContext&) { - UNREACHABLE(); +Result StandardUserSystemClockCore::Flush(const SystemClockContext&) { + UNIMPLEMENTED(); return ERROR_NOT_IMPLEMENTED; } -ResultCode StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) { - UNREACHABLE(); +Result StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) { + UNIMPLEMENTED(); return ERROR_NOT_IMPLEMENTED; } -ResultCode StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& system, - bool value) const { +Result StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& system, + bool value) const { if (auto_correction_enabled == value) { return ResultSuccess; } @@ -69,7 +68,7 @@ ResultCode StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& s } SystemClockContext ctx{}; - if (const ResultCode result{network_system_clock_core.GetClockContext(system, ctx)}; + if (const Result result{network_system_clock_core.GetClockContext(system, ctx)}; result != ResultSuccess) { return result; } diff --git a/src/core/hle/service/time/standard_user_system_clock_core.h b/src/core/hle/service/time/standard_user_system_clock_core.h index b7cb2b045..ee6e29487 100644 --- a/src/core/hle/service/time/standard_user_system_clock_core.h +++ b/src/core/hle/service/time/standard_user_system_clock_core.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -29,9 +28,9 @@ public: ~StandardUserSystemClockCore() override; - ResultCode SetAutomaticCorrectionEnabled(Core::System& system, bool value); + Result SetAutomaticCorrectionEnabled(Core::System& system, bool value); - ResultCode GetClockContext(Core::System& system, SystemClockContext& ctx) const override; + Result GetClockContext(Core::System& system, SystemClockContext& ctx) const override; bool IsAutomaticCorrectionEnabled() const { return auto_correction_enabled; @@ -42,11 +41,11 @@ public: } protected: - ResultCode Flush(const SystemClockContext&) override; + Result Flush(const SystemClockContext&) override; - ResultCode SetClockContext(const SystemClockContext&) override; + Result SetClockContext(const SystemClockContext&) override; - ResultCode ApplyAutomaticCorrection(Core::System& system, bool value) const; + Result ApplyAutomaticCorrection(Core::System& system, bool value) const; const SteadyClockTimePoint& GetAutomaticCorrectionUpdatedTime() const { return auto_correction_time; diff --git a/src/core/hle/service/time/steady_clock_core.h b/src/core/hle/service/time/steady_clock_core.h index 5ee2c0e0a..2867c351c 100644 --- a/src/core/hle/service/time/steady_clock_core.h +++ b/src/core/hle/service/time/steady_clock_core.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/system_clock_context_update_callback.cpp b/src/core/hle/service/time/system_clock_context_update_callback.cpp index f656fab1c..a649bed3a 100644 --- a/src/core/hle/service/time/system_clock_context_update_callback.cpp +++ b/src/core/hle/service/time/system_clock_context_update_callback.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/time/errors.h" @@ -31,8 +30,8 @@ void SystemClockContextUpdateCallback::BroadcastOperationEvent() { } } -ResultCode SystemClockContextUpdateCallback::Update(const SystemClockContext& value) { - ResultCode result{ResultSuccess}; +Result SystemClockContextUpdateCallback::Update(const SystemClockContext& value) { + Result result{ResultSuccess}; if (NeedUpdate(value)) { context = value; @@ -48,7 +47,7 @@ ResultCode SystemClockContextUpdateCallback::Update(const SystemClockContext& va return result; } -ResultCode SystemClockContextUpdateCallback::Update() { +Result SystemClockContextUpdateCallback::Update() { return ResultSuccess; } diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h index 6936397a5..9c6caf196 100644 --- a/src/core/hle/service/time/system_clock_context_update_callback.h +++ b/src/core/hle/service/time/system_clock_context_update_callback.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -29,10 +28,10 @@ public: void BroadcastOperationEvent(); - ResultCode Update(const SystemClockContext& value); + Result Update(const SystemClockContext& value); protected: - virtual ResultCode Update(); + virtual Result Update(); SystemClockContext context{}; diff --git a/src/core/hle/service/time/system_clock_core.cpp b/src/core/hle/service/time/system_clock_core.cpp index 5c2354cdd..da078241f 100644 --- a/src/core/hle/service/time/system_clock_core.cpp +++ b/src/core/hle/service/time/system_clock_core.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/time/steady_clock_core.h" #include "core/hle/service/time/system_clock_context_update_callback.h" @@ -15,13 +14,13 @@ SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core_) SystemClockCore::~SystemClockCore() = default; -ResultCode SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const { +Result SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const { posix_time = 0; const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; SystemClockContext clock_context{}; - if (const ResultCode result{GetClockContext(system, clock_context)}; result != ResultSuccess) { + if (const Result result{GetClockContext(system, clock_context)}; result != ResultSuccess) { return result; } @@ -34,26 +33,26 @@ ResultCode SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time return ResultSuccess; } -ResultCode SystemClockCore::SetCurrentTime(Core::System& system, s64 posix_time) { +Result SystemClockCore::SetCurrentTime(Core::System& system, s64 posix_time) { const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; const SystemClockContext clock_context{posix_time - current_time_point.time_point, current_time_point}; - if (const ResultCode result{SetClockContext(clock_context)}; result != ResultSuccess) { + if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) { return result; } return Flush(clock_context); } -ResultCode SystemClockCore::Flush(const SystemClockContext& clock_context) { +Result SystemClockCore::Flush(const SystemClockContext& clock_context) { if (!system_clock_context_update_callback) { return ResultSuccess; } return system_clock_context_update_callback->Update(clock_context); } -ResultCode SystemClockCore::SetSystemClockContext(const SystemClockContext& clock_context) { - if (const ResultCode result{SetClockContext(clock_context)}; result != ResultSuccess) { +Result SystemClockCore::SetSystemClockContext(const SystemClockContext& clock_context) { + if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) { return result; } return Flush(clock_context); diff --git a/src/core/hle/service/time/system_clock_core.h b/src/core/hle/service/time/system_clock_core.h index b9237ad28..8cb34126f 100644 --- a/src/core/hle/service/time/system_clock_core.h +++ b/src/core/hle/service/time/system_clock_core.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -30,28 +29,28 @@ public: return steady_clock_core; } - ResultCode GetCurrentTime(Core::System& system, s64& posix_time) const; + Result GetCurrentTime(Core::System& system, s64& posix_time) const; - ResultCode SetCurrentTime(Core::System& system, s64 posix_time); + Result SetCurrentTime(Core::System& system, s64 posix_time); - virtual ResultCode GetClockContext([[maybe_unused]] Core::System& system, - SystemClockContext& value) const { + virtual Result GetClockContext([[maybe_unused]] Core::System& system, + SystemClockContext& value) const { value = context; return ResultSuccess; } - virtual ResultCode SetClockContext(const SystemClockContext& value) { + virtual Result SetClockContext(const SystemClockContext& value) { context = value; return ResultSuccess; } - virtual ResultCode Flush(const SystemClockContext& clock_context); + virtual Result Flush(const SystemClockContext& clock_context); void SetUpdateCallbackInstance(std::shared_ptr<SystemClockContextUpdateCallback> callback) { system_clock_context_update_callback = std::move(callback); } - ResultCode SetSystemClockContext(const SystemClockContext& context); + Result SetSystemClockContext(const SystemClockContext& context); bool IsInitialized() const { return is_initialized; diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp index 47d4ab980..27600413e 100644 --- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp +++ b/src/core/hle/service/time/tick_based_steady_clock_core.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/core_timing.h" diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.h b/src/core/hle/service/time/tick_based_steady_clock_core.h index 1a5a53fd7..491185dc3 100644 --- a/src/core/hle/service/time/tick_based_steady_clock_core.h +++ b/src/core/hle/service/time/tick_based_steady_clock_core.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 4d8823b5a..f77cdbb43 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/core.h" @@ -44,8 +43,7 @@ private: } s64 posix_time{}; - if (const ResultCode result{clock_core.GetCurrentTime(system, posix_time)}; - result.IsError()) { + if (const Result result{clock_core.GetCurrentTime(system, posix_time)}; result.IsError()) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); return; @@ -66,7 +64,7 @@ private: } Clock::SystemClockContext system_clock_context{}; - if (const ResultCode result{clock_core.GetClockContext(system, system_clock_context)}; + if (const Result result{clock_core.GetClockContext(system, system_clock_context)}; result.IsError()) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); @@ -117,7 +115,7 @@ private: Clock::SteadyClockCore& clock_core; }; -ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( +Result Module::Interface::GetClockSnapshotFromSystemClockContextInternal( Kernel::KThread* thread, Clock::SystemClockContext user_context, Clock::SystemClockContext network_context, Clock::TimeType type, Clock::ClockSnapshot& clock_snapshot) { @@ -130,7 +128,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled(); clock_snapshot.type = type; - if (const ResultCode result{ + if (const Result result{ time_manager.GetTimeZoneContentManager().GetTimeZoneManager().GetDeviceLocationName( clock_snapshot.location_name)}; result != ResultSuccess) { @@ -139,7 +137,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( clock_snapshot.user_context = user_context; - if (const ResultCode result{Clock::ClockSnapshot::GetCurrentTime( + if (const Result result{Clock::ClockSnapshot::GetCurrentTime( clock_snapshot.user_time, clock_snapshot.steady_clock_time_point, clock_snapshot.user_context)}; result != ResultSuccess) { @@ -147,7 +145,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( } TimeZone::CalendarInfo userCalendarInfo{}; - if (const ResultCode result{ + if (const Result result{ time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules( clock_snapshot.user_time, userCalendarInfo)}; result != ResultSuccess) { @@ -166,7 +164,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( } TimeZone::CalendarInfo networkCalendarInfo{}; - if (const ResultCode result{ + if (const Result result{ time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules( clock_snapshot.network_time, networkCalendarInfo)}; result != ResultSuccess) { @@ -263,7 +261,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called, type={}", type); Clock::SystemClockContext user_context{}; - if (const ResultCode result{ + if (const Result result{ system.GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(system, user_context)}; result.IsError()) { @@ -273,7 +271,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { } Clock::SystemClockContext network_context{}; - if (const ResultCode result{ + if (const Result result{ system.GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext( system, network_context)}; result.IsError()) { @@ -283,7 +281,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { } Clock::ClockSnapshot clock_snapshot{}; - if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal( + if (const Result result{GetClockSnapshotFromSystemClockContextInternal( &ctx.GetThread(), user_context, network_context, type, clock_snapshot)}; result.IsError()) { IPC::ResponseBuilder rb{ctx, 2}; @@ -309,7 +307,7 @@ void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLEReques LOG_DEBUG(Service_Time, "called, type={}", type); Clock::ClockSnapshot clock_snapshot{}; - if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal( + if (const Result result{GetClockSnapshotFromSystemClockContextInternal( &ctx.GetThread(), user_context, network_context, type, clock_snapshot)}; result != ResultSuccess) { IPC::ResponseBuilder rb{ctx, 2}; @@ -366,7 +364,7 @@ void Module::Interface::CalculateSpanBetween(Kernel::HLERequestContext& ctx) { Clock::TimeSpanType time_span_type{}; s64 span{}; - if (const ResultCode result{snapshot_a.steady_clock_time_point.GetSpanBetween( + if (const Result result{snapshot_a.steady_clock_time_point.GetSpanBetween( snapshot_b.steady_clock_time_point, span)}; result != ResultSuccess) { if (snapshot_a.network_time && snapshot_b.network_time) { diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index 30e2cd369..76a46cfc7 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -37,7 +36,7 @@ public: void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx); private: - ResultCode GetClockSnapshotFromSystemClockContextInternal( + Result GetClockSnapshotFromSystemClockContextInternal( Kernel::KThread* thread, Clock::SystemClockContext user_context, Clock::SystemClockContext network_context, Clock::TimeType type, Clock::ClockSnapshot& cloc_snapshot); diff --git a/src/core/hle/service/time/time_interface.cpp b/src/core/hle/service/time/time_interface.cpp index bb7b6b5c1..0c53e98ee 100644 --- a/src/core/hle/service/time/time_interface.cpp +++ b/src/core/hle/service/time/time_interface.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/time/time_interface.h" diff --git a/src/core/hle/service/time/time_interface.h b/src/core/hle/service/time/time_interface.h index c41766f1a..ceeb0e5ef 100644 --- a/src/core/hle/service/time/time_interface.h +++ b/src/core/hle/service/time/time_interface.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp index 00f1ae8cf..28667710e 100644 --- a/src/core/hle/service/time/time_manager.cpp +++ b/src/core/hle/service/time/time_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <chrono> #include <ctime> @@ -112,7 +111,7 @@ struct TimeManager::Impl final { FileSys::VirtualFile& vfs_file) { if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule( location_name, vfs_file) != ResultSuccess) { - UNREACHABLE(); + ASSERT(false); return; } @@ -156,7 +155,7 @@ struct TimeManager::Impl final { } else { if (standard_local_system_clock_core.SetCurrentTime(system_, posix_time) != ResultSuccess) { - UNREACHABLE(); + ASSERT(false); return; } } @@ -171,7 +170,7 @@ struct TimeManager::Impl final { if (standard_network_system_clock_core.SetSystemClockContext(clock_context) != ResultSuccess) { - UNREACHABLE(); + ASSERT(false); return; } @@ -184,7 +183,7 @@ struct TimeManager::Impl final { Clock::SteadyClockTimePoint steady_clock_time_point) { if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled( system_, is_automatic_correction_enabled) != ResultSuccess) { - UNREACHABLE(); + ASSERT(false); return; } @@ -204,7 +203,7 @@ struct TimeManager::Impl final { if (GetStandardLocalSystemClockCore() .SetCurrentTime(system_, timespan.ToSeconds()) .IsError()) { - UNREACHABLE(); + ASSERT(false); return; } } diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h index 2404067c0..4f046f266 100644 --- a/src/core/hle/service/time/time_manager.h +++ b/src/core/hle/service/time/time_manager.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp index ed9f75ed6..a3aa0e77f 100644 --- a/src/core/hle/service/time/time_sharedmemory.cpp +++ b/src/core/hle/service/time/time_sharedmemory.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/core_timing.h" diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h index 9307ea795..561685acd 100644 --- a/src/core/hle/service/time/time_sharedmemory.h +++ b/src/core/hle/service/time/time_sharedmemory.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp index c634b6abd..afbfe9715 100644 --- a/src/core/hle/service/time/time_zone_content_manager.cpp +++ b/src/core/hle/service/time/time_zone_content_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <sstream> @@ -91,10 +90,10 @@ void TimeZoneContentManager::Initialize(TimeManager& time_manager) { } } -ResultCode TimeZoneContentManager::LoadTimeZoneRule(TimeZoneRule& rules, - const std::string& location_name) const { +Result TimeZoneContentManager::LoadTimeZoneRule(TimeZoneRule& rules, + const std::string& location_name) const { FileSys::VirtualFile vfs_file; - if (const ResultCode result{GetTimeZoneInfoFile(location_name, vfs_file)}; + if (const Result result{GetTimeZoneInfoFile(location_name, vfs_file)}; result != ResultSuccess) { return result; } @@ -107,8 +106,8 @@ bool TimeZoneContentManager::IsLocationNameValid(const std::string& location_nam location_name_cache.end(); } -ResultCode TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_name, - FileSys::VirtualFile& vfs_file) const { +Result TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_name, + FileSys::VirtualFile& vfs_file) const { if (!IsLocationNameValid(location_name)) { return ERROR_TIME_NOT_FOUND; } diff --git a/src/core/hle/service/time/time_zone_content_manager.h b/src/core/hle/service/time/time_zone_content_manager.h index cfa601084..3d94b6428 100644 --- a/src/core/hle/service/time/time_zone_content_manager.h +++ b/src/core/hle/service/time/time_zone_content_manager.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -33,12 +32,12 @@ public: return time_zone_manager; } - ResultCode LoadTimeZoneRule(TimeZoneRule& rules, const std::string& location_name) const; + Result LoadTimeZoneRule(TimeZoneRule& rules, const std::string& location_name) const; private: bool IsLocationNameValid(const std::string& location_name) const; - ResultCode GetTimeZoneInfoFile(const std::string& location_name, - FileSys::VirtualFile& vfs_file) const; + Result GetTimeZoneInfoFile(const std::string& location_name, + FileSys::VirtualFile& vfs_file) const; Core::System& system; TimeZoneManager time_zone_manager; diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp index 2989cee5e..2aa675df9 100644 --- a/src/core/hle/service/time/time_zone_manager.cpp +++ b/src/core/hle/service/time/time_zone_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <climits> @@ -111,10 +110,9 @@ static constexpr s64 GetLeapDaysFromYear(s64 year) { } } -static constexpr int GetMonthLength(bool is_leap_year, int month) { - constexpr std::array<int, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - constexpr std::array<int, 12> month_lengths_leap{31, 29, 31, 30, 31, 30, - 31, 31, 30, 31, 30, 31}; +static constexpr s8 GetMonthLength(bool is_leap_year, int month) { + constexpr std::array<s8, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + constexpr std::array<s8, 12> month_lengths_leap{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; return is_leap_year ? month_lengths_leap[month] : month_lengths[month]; } @@ -281,7 +279,7 @@ static constexpr int TransitionTime(int year, Rule rule, int offset) { break; } default: - UNREACHABLE(); + ASSERT(false); } return value + rule.transition_time + offset; } @@ -668,8 +666,8 @@ static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFi return true; } -static ResultCode CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& calendar_time, - CalendarAdditionalInfo& calendar_additional_info) { +static Result CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& calendar_time, + CalendarAdditionalInfo& calendar_additional_info) { s64 year{epoch_year}; s64 time_days{time / seconds_per_day}; s64 remaining_seconds{time % seconds_per_day}; @@ -743,9 +741,9 @@ static ResultCode CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInter return ResultSuccess; } -static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, - CalendarTimeInternal& calendar_time, - CalendarAdditionalInfo& calendar_additional_info) { +static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, + CalendarTimeInternal& calendar_time, + CalendarAdditionalInfo& calendar_additional_info) { if ((rules.go_ahead && time < rules.ats[0]) || (rules.go_back && time > rules.ats[rules.time_count - 1])) { s64 seconds{}; @@ -768,7 +766,7 @@ static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, if (new_time < rules.ats[0] && new_time > rules.ats[rules.time_count - 1]) { return ERROR_TIME_NOT_FOUND; } - if (const ResultCode result{ + if (const Result result{ ToCalendarTimeInternal(rules, new_time, calendar_time, calendar_additional_info)}; result != ResultSuccess) { return result; @@ -799,8 +797,8 @@ static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, tti_index = rules.types[low - 1]; } - if (const ResultCode result{CreateCalendarTime(time, rules.ttis[tti_index].gmt_offset, - calendar_time, calendar_additional_info)}; + if (const Result result{CreateCalendarTime(time, rules.ttis[tti_index].gmt_offset, + calendar_time, calendar_additional_info)}; result != ResultSuccess) { return result; } @@ -813,9 +811,9 @@ static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, return ResultSuccess; } -static ResultCode ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) { +static Result ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) { CalendarTimeInternal calendar_time{}; - const ResultCode result{ + const Result result{ ToCalendarTimeInternal(rules, time, calendar_time, calendar.additional_info)}; calendar.time.year = static_cast<s16>(calendar_time.year); @@ -832,13 +830,13 @@ static ResultCode ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, Calend TimeZoneManager::TimeZoneManager() = default; TimeZoneManager::~TimeZoneManager() = default; -ResultCode TimeZoneManager::ToCalendarTime(const TimeZoneRule& rules, s64 time, - CalendarInfo& calendar) const { +Result TimeZoneManager::ToCalendarTime(const TimeZoneRule& rules, s64 time, + CalendarInfo& calendar) const { return ToCalendarTimeImpl(rules, time, calendar); } -ResultCode TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name, - FileSys::VirtualFile& vfs_file) { +Result TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name, + FileSys::VirtualFile& vfs_file) { TimeZoneRule rule{}; if (ParseTimeZoneBinary(rule, vfs_file)) { device_location_name = location_name; @@ -848,12 +846,12 @@ ResultCode TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::str return ERROR_TIME_ZONE_CONVERSION_FAILED; } -ResultCode TimeZoneManager::SetUpdatedTime(const Clock::SteadyClockTimePoint& value) { +Result TimeZoneManager::SetUpdatedTime(const Clock::SteadyClockTimePoint& value) { time_zone_update_time_point = value; return ResultSuccess; } -ResultCode TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const { +Result TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const { if (is_initialized) { return ToCalendarTime(time_zone_rule, time, calendar); } else { @@ -861,16 +859,16 @@ ResultCode TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& ca } } -ResultCode TimeZoneManager::ParseTimeZoneRuleBinary(TimeZoneRule& rules, - FileSys::VirtualFile& vfs_file) const { +Result TimeZoneManager::ParseTimeZoneRuleBinary(TimeZoneRule& rules, + FileSys::VirtualFile& vfs_file) const { if (!ParseTimeZoneBinary(rules, vfs_file)) { return ERROR_TIME_ZONE_CONVERSION_FAILED; } return ResultSuccess; } -ResultCode TimeZoneManager::ToPosixTime(const TimeZoneRule& rules, - const CalendarTime& calendar_time, s64& posix_time) const { +Result TimeZoneManager::ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time, + s64& posix_time) const { posix_time = 0; CalendarTimeInternal internal_time{ @@ -1022,8 +1020,8 @@ ResultCode TimeZoneManager::ToPosixTime(const TimeZoneRule& rules, return ResultSuccess; } -ResultCode TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_time, - s64& posix_time) const { +Result TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_time, + s64& posix_time) const { if (is_initialized) { return ToPosixTime(time_zone_rule, calendar_time, posix_time); } @@ -1031,7 +1029,7 @@ ResultCode TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_t return ERROR_UNINITIALIZED_CLOCK; } -ResultCode TimeZoneManager::GetDeviceLocationName(LocationName& value) const { +Result TimeZoneManager::GetDeviceLocationName(LocationName& value) const { if (!is_initialized) { return ERROR_UNINITIALIZED_CLOCK; } diff --git a/src/core/hle/service/time/time_zone_manager.h b/src/core/hle/service/time/time_zone_manager.h index aaab0a1e0..5ebd4035e 100644 --- a/src/core/hle/service/time/time_zone_manager.h +++ b/src/core/hle/service/time/time_zone_manager.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -30,16 +29,16 @@ public: is_initialized = true; } - ResultCode SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name, - FileSys::VirtualFile& vfs_file); - ResultCode SetUpdatedTime(const Clock::SteadyClockTimePoint& value); - ResultCode GetDeviceLocationName(TimeZone::LocationName& value) const; - ResultCode ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const; - ResultCode ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const; - ResultCode ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const; - ResultCode ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time, - s64& posix_time) const; - ResultCode ToPosixTimeWithMyRule(const CalendarTime& calendar_time, s64& posix_time) const; + Result SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name, + FileSys::VirtualFile& vfs_file); + Result SetUpdatedTime(const Clock::SteadyClockTimePoint& value); + Result GetDeviceLocationName(TimeZone::LocationName& value) const; + Result ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const; + Result ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const; + Result ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const; + Result ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time, + s64& posix_time) const; + Result ToPosixTimeWithMyRule(const CalendarTime& calendar_time, s64& posix_time) const; private: bool is_initialized{}; diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp index 3871e7316..961040bfc 100644 --- a/src/core/hle/service/time/time_zone_service.cpp +++ b/src/core/hle/service/time/time_zone_service.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" @@ -33,7 +32,7 @@ void ITimeZoneService::GetDeviceLocationName(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called"); TimeZone::LocationName location_name{}; - if (const ResultCode result{ + if (const Result result{ time_zone_content_manager.GetTimeZoneManager().GetDeviceLocationName(location_name)}; result != ResultSuccess) { IPC::ResponseBuilder rb{ctx, 2}; @@ -62,7 +61,7 @@ void ITimeZoneService::LoadTimeZoneRule(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called, location_name={}", location_name); TimeZone::TimeZoneRule time_zone_rule{}; - if (const ResultCode result{ + if (const Result result{ time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)}; result != ResultSuccess) { IPC::ResponseBuilder rb{ctx, 2}; @@ -89,7 +88,7 @@ void ITimeZoneService::ToCalendarTime(Kernel::HLERequestContext& ctx) { std::memcpy(&time_zone_rule, buffer.data(), buffer.size()); TimeZone::CalendarInfo calendar_info{}; - if (const ResultCode result{time_zone_content_manager.GetTimeZoneManager().ToCalendarTime( + if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToCalendarTime( time_zone_rule, posix_time, calendar_info)}; result != ResultSuccess) { IPC::ResponseBuilder rb{ctx, 2}; @@ -109,7 +108,7 @@ void ITimeZoneService::ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx) LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time); TimeZone::CalendarInfo calendar_info{}; - if (const ResultCode result{ + if (const Result result{ time_zone_content_manager.GetTimeZoneManager().ToCalendarTimeWithMyRules( posix_time, calendar_info)}; result != ResultSuccess) { @@ -132,7 +131,7 @@ void ITimeZoneService::ToPosixTime(Kernel::HLERequestContext& ctx) { std::memcpy(&time_zone_rule, ctx.ReadBuffer().data(), sizeof(TimeZone::TimeZoneRule)); s64 posix_time{}; - if (const ResultCode result{time_zone_content_manager.GetTimeZoneManager().ToPosixTime( + if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTime( time_zone_rule, calendar_time, posix_time)}; result != ResultSuccess) { IPC::ResponseBuilder rb{ctx, 2}; @@ -155,9 +154,8 @@ void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) { const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()}; s64 posix_time{}; - if (const ResultCode result{ - time_zone_content_manager.GetTimeZoneManager().ToPosixTimeWithMyRule(calendar_time, - posix_time)}; + if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTimeWithMyRule( + calendar_time, posix_time)}; result != ResultSuccess) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); diff --git a/src/core/hle/service/time/time_zone_service.h b/src/core/hle/service/time/time_zone_service.h index 2c9b97603..f151f4b56 100644 --- a/src/core/hle/service/time/time_zone_service.h +++ b/src/core/hle/service/time/time_zone_service.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/time_zone_types.h b/src/core/hle/service/time/time_zone_types.h index d39103253..eb4fb52d1 100644 --- a/src/core/hle/service/time/time_zone_types.h +++ b/src/core/hle/service/time/time_zone_types.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp index 0747c33cd..ac46a406c 100644 --- a/src/core/hle/service/usb/usb.cpp +++ b/src/core/hle/service/usb/usb.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/usb/usb.h b/src/core/hle/service/usb/usb.h index fc366df34..b41b9684c 100644 --- a/src/core/hle/service/usb/usb.h +++ b/src/core/hle/service/usb/usb.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index b7705c02a..288aafaaf 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <utility> @@ -13,14 +12,38 @@ #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvflinger/buffer_item_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_core.h" +#include "core/hle/service/nvflinger/buffer_queue_producer.h" +#include "core/hle/service/nvflinger/hos_binder_driver_server.h" #include "core/hle/service/vi/display/vi_display.h" #include "core/hle/service/vi/layer/vi_layer.h" +#include "core/hle/service/vi/vi_results.h" namespace Service::VI { -Display::Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, - Core::System& system_) - : display_id{id}, name{std::move(name_)}, service_context{service_context_} { +struct BufferQueue { + std::shared_ptr<android::BufferQueueCore> core; + std::unique_ptr<android::BufferQueueProducer> producer; + std::unique_ptr<android::BufferQueueConsumer> consumer; +}; + +static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context, + Service::Nvidia::NvCore::NvMap& nvmap) { + auto buffer_queue_core = std::make_shared<android::BufferQueueCore>(); + return { + buffer_queue_core, + std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap), + std::make_unique<android::BufferQueueConsumer>(buffer_queue_core, nvmap)}; +} + +Display::Display(u64 id, std::string name_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_, + KernelHelpers::ServiceContext& service_context_, Core::System& system_) + : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_}, + service_context{service_context_} { vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); } @@ -36,29 +59,48 @@ const Layer& Display::GetLayer(std::size_t index) const { return *layers.at(index); } -Kernel::KReadableEvent& Display::GetVSyncEvent() { - return vsync_event->GetReadableEvent(); +ResultVal<Kernel::KReadableEvent*> Display::GetVSyncEvent() { + if (got_vsync_event) { + return ResultPermissionDenied; + } + + got_vsync_event = true; + + return GetVSyncEventUnchecked(); +} + +Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() { + return &vsync_event->GetReadableEvent(); } void Display::SignalVSyncEvent() { vsync_event->GetWritableEvent().Signal(); } -void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) { - // TODO(Subv): Support more than 1 layer. +void Display::CreateLayer(u64 layer_id, u32 binder_id, + Service::Nvidia::NvCore::Container& nv_core) { ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); - layers.emplace_back(std::make_shared<Layer>(layer_id, buffer_queue)); + auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile()); + + auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer)); + buffer_item_consumer->Connect(false); + + layers.emplace_back(std::make_unique<Layer>(layer_id, binder_id, *core, *producer, + std::move(buffer_item_consumer))); + + hos_binder_driver_server.RegisterProducer(std::move(producer)); } void Display::CloseLayer(u64 layer_id) { - std::erase_if(layers, [layer_id](const auto& layer) { return layer->GetID() == layer_id; }); + std::erase_if(layers, + [layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; }); } Layer* Display::FindLayer(u64 layer_id) { const auto itr = - std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) { - return layer->GetID() == layer_id; + std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) { + return layer->GetLayerId() == layer_id; }); if (itr == layers.end()) { @@ -70,8 +112,8 @@ Layer* Display::FindLayer(u64 layer_id) { const Layer* Display::FindLayer(u64 layer_id) const { const auto itr = - std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) { - return layer->GetID() == layer_id; + std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) { + return layer->GetLayerId() == layer_id; }); if (itr == layers.end()) { diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 329f4ba86..33d5f398c 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,17 +9,28 @@ #include "common/common_funcs.h" #include "common/common_types.h" +#include "core/hle/result.h" namespace Kernel { class KEvent; } -namespace Service::NVFlinger { -class BufferQueue; +namespace Service::android { +class BufferQueueProducer; } + namespace Service::KernelHelpers { class ServiceContext; -} // namespace Service::KernelHelpers +} + +namespace Service::NVFlinger { +class HosBinderDriverServer; +} + +namespace Service::Nvidia::NvCore { +class Container; +class NvMap; +} // namespace Service::Nvidia::NvCore namespace Service::VI { @@ -35,12 +45,13 @@ public: /// Constructs a display with a given unique ID and name. /// /// @param id The unique ID for this display. + /// @param hos_binder_driver_server_ NVFlinger HOSBinderDriver server instance. /// @param service_context_ The ServiceContext for the owning service. /// @param name_ The name for this display. /// @param system_ The global system instance. /// - Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, - Core::System& system_); + Display(u64 id, std::string name_, NVFlinger::HosBinderDriverServer& hos_binder_driver_server_, + KernelHelpers::ServiceContext& service_context_, Core::System& system_); ~Display(); /// Gets the unique ID assigned to this display. @@ -64,18 +75,30 @@ public: /// Gets a layer for this display based off an index. const Layer& GetLayer(std::size_t index) const; - /// Gets the readable vsync event. - Kernel::KReadableEvent& GetVSyncEvent(); + std::size_t GetNumLayers() const { + return layers.size(); + } + + /** + * Gets the internal vsync event. + * + * @returns The internal Vsync event if it has not yet been retrieved, + * VI::ResultPermissionDenied otherwise. + */ + [[nodiscard]] ResultVal<Kernel::KReadableEvent*> GetVSyncEvent(); + + /// Gets the internal vsync event. + Kernel::KReadableEvent* GetVSyncEventUnchecked(); /// Signals the internal vsync event. void SignalVSyncEvent(); /// Creates and adds a layer to this display with the given ID. /// - /// @param layer_id The ID to assign to the created layer. - /// @param buffer_queue The buffer queue for the layer instance to use. + /// @param layer_id The ID to assign to the created layer. + /// @param binder_id The ID assigned to the buffer queue. /// - void CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue); + void CreateLayer(u64 layer_id, u32 binder_id, Service::Nvidia::NvCore::Container& core); /// Closes and removes a layer from this display with the given ID. /// @@ -104,10 +127,12 @@ public: private: u64 display_id; std::string name; + NVFlinger::HosBinderDriverServer& hos_binder_driver_server; KernelHelpers::ServiceContext& service_context; - std::vector<std::shared_ptr<Layer>> layers; + std::vector<std::unique_ptr<Layer>> layers; Kernel::KEvent* vsync_event{}; + bool got_vsync_event{false}; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp index 9bc382587..9ae2e0e44 100644 --- a/src/core/hle/service/vi/layer/vi_layer.cpp +++ b/src/core/hle/service/vi/layer/vi_layer.cpp @@ -1,12 +1,15 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/vi/layer/vi_layer.h" namespace Service::VI { -Layer::Layer(u64 id, NVFlinger::BufferQueue& queue) : layer_id{id}, buffer_queue{queue} {} +Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_, + android::BufferQueueProducer& binder_, + std::shared_ptr<android::BufferItemConsumer>&& consumer_) + : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move( + consumer_)} {} Layer::~Layer() = default; diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h index ebdd85505..8cf1b5275 100644 --- a/src/core/hle/service/vi/layer/vi_layer.h +++ b/src/core/hle/service/vi/layer/vi_layer.h @@ -1,14 +1,17 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include <memory> + #include "common/common_types.h" -namespace Service::NVFlinger { -class BufferQueue; -} +namespace Service::android { +class BufferItemConsumer; +class BufferQueueCore; +class BufferQueueProducer; +} // namespace Service::android namespace Service::VI { @@ -17,10 +20,13 @@ class Layer { public: /// Constructs a layer with a given ID and buffer queue. /// - /// @param id The ID to assign to this layer. - /// @param queue The buffer queue for this layer to use. + /// @param layer_id_ The ID to assign to this layer. + /// @param binder_id_ The binder ID to assign to this layer. + /// @param binder_ The buffer producer queue for this layer to use. /// - Layer(u64 id, NVFlinger::BufferQueue& queue); + Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_, + android::BufferQueueProducer& binder_, + std::shared_ptr<android::BufferItemConsumer>&& consumer_); ~Layer(); Layer(const Layer&) = delete; @@ -30,23 +36,47 @@ public: Layer& operator=(Layer&&) = delete; /// Gets the ID for this layer. - u64 GetID() const { + u64 GetLayerId() const { return layer_id; } + /// Gets the binder ID for this layer. + u32 GetBinderId() const { + return binder_id; + } + /// Gets a reference to the buffer queue this layer is using. - NVFlinger::BufferQueue& GetBufferQueue() { - return buffer_queue; + android::BufferQueueProducer& GetBufferQueue() { + return binder; } /// Gets a const reference to the buffer queue this layer is using. - const NVFlinger::BufferQueue& GetBufferQueue() const { - return buffer_queue; + const android::BufferQueueProducer& GetBufferQueue() const { + return binder; + } + + android::BufferItemConsumer& GetConsumer() { + return *consumer; + } + + const android::BufferItemConsumer& GetConsumer() const { + return *consumer; + } + + android::BufferQueueCore& Core() { + return core; + } + + const android::BufferQueueCore& Core() const { + return core; } private: - u64 layer_id; - NVFlinger::BufferQueue& buffer_queue; + const u64 layer_id; + const u32 binder_id; + android::BufferQueueCore& core; + android::BufferQueueProducer& binder; + std::shared_ptr<android::BufferItemConsumer> consumer; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 75ee3e5e4..9c917cacf 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> @@ -22,21 +21,20 @@ #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/service/nvdrv/nvdata.h" -#include "core/hle/service/nvflinger/buffer_queue.h" +#include "core/hle/service/nvflinger/binder.h" +#include "core/hle/service/nvflinger/buffer_queue_producer.h" +#include "core/hle/service/nvflinger/hos_binder_driver_server.h" #include "core/hle/service/nvflinger/nvflinger.h" +#include "core/hle/service/nvflinger/parcel.h" #include "core/hle/service/service.h" #include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi_m.h" +#include "core/hle/service/vi/vi_results.h" #include "core/hle/service/vi/vi_s.h" #include "core/hle/service/vi/vi_u.h" namespace Service::VI { -constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1}; -constexpr ResultCode ERR_PERMISSION_DENIED{ErrorModule::VI, 5}; -constexpr ResultCode ERR_UNSUPPORTED{ErrorModule::VI, 6}; -constexpr ResultCode ERR_NOT_FOUND{ErrorModule::VI, 7}; - struct DisplayInfo { /// The name of this particular display. char display_name[0x40]{"Default"}; @@ -57,447 +55,26 @@ struct DisplayInfo { }; static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size"); -class Parcel { -public: - // This default size was chosen arbitrarily. - static constexpr std::size_t DefaultBufferSize = 0x40; - Parcel() : buffer(DefaultBufferSize) {} - explicit Parcel(std::vector<u8> data) : buffer(std::move(data)) {} - virtual ~Parcel() = default; - - template <typename T> - T Read() { - static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); - ASSERT(read_index + sizeof(T) <= buffer.size()); - - T val; - std::memcpy(&val, buffer.data() + read_index, sizeof(T)); - read_index += sizeof(T); - read_index = Common::AlignUp(read_index, 4); - return val; - } - - template <typename T> - T ReadUnaligned() { - static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); - ASSERT(read_index + sizeof(T) <= buffer.size()); - - T val; - std::memcpy(&val, buffer.data() + read_index, sizeof(T)); - read_index += sizeof(T); - return val; - } - - std::vector<u8> ReadBlock(std::size_t 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; - } - - std::u16string ReadInterfaceToken() { - [[maybe_unused]] const u32 unknown = Read<u32_le>(); - const u32 length = Read<u32_le>(); - - std::u16string token{}; - - for (u32 ch = 0; ch < length + 1; ++ch) { - token.push_back(ReadUnaligned<u16_le>()); - } - - read_index = Common::AlignUp(read_index, 4); - - return token; - } - - template <typename T> - void Write(const T& val) { - static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); - - if (buffer.size() < write_index + sizeof(T)) { - buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize); - } - - std::memcpy(buffer.data() + write_index, &val, sizeof(T)); - write_index += sizeof(T); - write_index = Common::AlignUp(write_index, 4); - } - - template <typename T> - void WriteObject(const T& val) { - static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); - - const 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)); - - read_index = header.data_offset; - DeserializeData(); - } - - std::vector<u8> Serialize() { - ASSERT(read_index == 0); - write_index = sizeof(Header); - - SerializeData(); - - Header header{}; - header.data_size = static_cast<u32_le>(write_index - sizeof(Header)); - header.data_offset = sizeof(Header); - header.objects_size = 4; - header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size); - std::memcpy(buffer.data(), &header, sizeof(Header)); - - return buffer; - } - -protected: - virtual void SerializeData() {} - - virtual void DeserializeData() {} - -private: - struct Header { - u32_le data_size; - u32_le data_offset; - u32_le objects_size; - u32_le objects_offset; - }; - static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size"); - - std::vector<u8> buffer; - std::size_t read_index = 0; - std::size_t write_index = 0; -}; - -class NativeWindow : public Parcel { -public: - explicit NativeWindow(u32 id) { - data.id = id; - } - ~NativeWindow() override = default; - -protected: - void SerializeData() override { - Write(data); - } - -private: - struct Data { - u32_le magic = 2; - u32_le process_id = 1; - u32_le id; - 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"); - - Data data{}; -}; - -class IGBPConnectRequestParcel : public Parcel { -public: - explicit IGBPConnectRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - data = Read<Data>(); - } - - struct Data { - u32_le unk; - u32_le api; - u32_le producer_controlled_by_app; - }; - - Data data; -}; - -class IGBPConnectResponseParcel : public Parcel { +class NativeWindow final { public: - explicit IGBPConnectResponseParcel(u32 width, u32 height) { - data.width = width; - data.height = height; - } - ~IGBPConnectResponseParcel() override = default; - -protected: - void SerializeData() override { - Write(data); - } - -private: - struct Data { - u32_le width; - u32_le height; - u32_le transform_hint; - u32_le num_pending_buffers; - u32_le status; - }; - static_assert(sizeof(Data) == 20, "ParcelData has wrong size"); - - Data data{}; -}; - -/// Represents a parcel containing one int '0' as its data -/// Used by DetachBuffer and Disconnect -class IGBPEmptyResponseParcel : public Parcel { -protected: - void SerializeData() override { - Write(data); - } + constexpr explicit NativeWindow(u32 id_) : id{id_} {} + constexpr explicit NativeWindow(const NativeWindow& other) = default; private: - struct Data { - u32_le unk_0{}; - }; - - Data data{}; -}; - -class IGBPSetPreallocatedBufferRequestParcel : public Parcel { -public: - explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer_) - : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - data = Read<Data>(); - if (data.contains_object != 0) { - buffer_container = Read<BufferContainer>(); - } - } - - struct Data { - u32_le slot; - u32_le contains_object; - }; - - struct BufferContainer { - u32_le graphic_buffer_length; - INSERT_PADDING_WORDS(1); - NVFlinger::IGBPBuffer buffer{}; - }; - - Data data{}; - BufferContainer buffer_container{}; -}; - -class IGBPSetPreallocatedBufferResponseParcel : public Parcel { -protected: - void SerializeData() override { - // TODO(Subv): Find out what this means - Write<u32>(0); - } -}; - -class IGBPCancelBufferRequestParcel : public Parcel { -public: - explicit IGBPCancelBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - data = Read<Data>(); - } - - struct Data { - u32_le slot; - Service::Nvidia::MultiFence multi_fence; - }; - - Data data; -}; - -class IGBPCancelBufferResponseParcel : public Parcel { -protected: - void SerializeData() override { - Write<u32>(0); // Success - } -}; - -class IGBPDequeueBufferRequestParcel : public Parcel { -public: - explicit IGBPDequeueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - data = Read<Data>(); - } - - struct Data { - u32_le pixel_format; - u32_le width; - u32_le height; - u32_le get_frame_timestamps; - u32_le usage; - }; - - Data data; -}; - -class IGBPDequeueBufferResponseParcel : public Parcel { -public: - explicit IGBPDequeueBufferResponseParcel(u32 slot_, Nvidia::MultiFence& multi_fence_) - : slot(slot_), multi_fence(multi_fence_) {} - -protected: - void SerializeData() override { - Write(slot); - Write<u32_le>(1); - WriteObject(multi_fence); - Write<u32_le>(0); - } - - u32_le slot; - Service::Nvidia::MultiFence multi_fence; -}; - -class IGBPRequestBufferRequestParcel : public Parcel { -public: - explicit IGBPRequestBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - slot = Read<u32_le>(); - } - - u32_le slot; -}; - -class IGBPRequestBufferResponseParcel : public Parcel { -public: - explicit IGBPRequestBufferResponseParcel(NVFlinger::IGBPBuffer buffer_) : buffer(buffer_) {} - ~IGBPRequestBufferResponseParcel() override = default; - -protected: - void SerializeData() override { - // 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); - WriteObject(buffer); - Write<u32_le>(0); - } - - NVFlinger::IGBPBuffer buffer; -}; - -class IGBPQueueBufferRequestParcel : public Parcel { -public: - explicit IGBPQueueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - data = Read<Data>(); - } - - struct Data { - u32_le slot; - INSERT_PADDING_WORDS(3); - u32_le timestamp; - s32_le is_auto_timestamp; - s32_le crop_top; - s32_le crop_left; - s32_le crop_right; - s32_le crop_bottom; - s32_le scaling_mode; - NVFlinger::BufferQueue::BufferTransformFlags transform; - u32_le sticky_transform; - INSERT_PADDING_WORDS(1); - u32_le swap_interval; - Service::Nvidia::MultiFence multi_fence; - - Common::Rectangle<int> GetCropRect() const { - return {crop_left, crop_top, crop_right, crop_bottom}; - } - }; - static_assert(sizeof(Data) == 96, "ParcelData has wrong size"); - - Data data; -}; - -class IGBPQueueBufferResponseParcel : public Parcel { -public: - explicit IGBPQueueBufferResponseParcel(u32 width, u32 height) { - data.width = width; - data.height = height; - } - ~IGBPQueueBufferResponseParcel() override = default; - -protected: - void SerializeData() override { - Write(data); - } - -private: - struct Data { - u32_le width; - u32_le height; - u32_le transform_hint; - u32_le num_pending_buffers; - u32_le status; - }; - static_assert(sizeof(Data) == 20, "ParcelData has wrong size"); - - Data data{}; -}; - -class IGBPQueryRequestParcel : public Parcel { -public: - explicit IGBPQueryRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - type = Read<u32_le>(); - } - - u32 type; -}; - -class IGBPQueryResponseParcel : public Parcel { -public: - explicit IGBPQueryResponseParcel(u32 value_) : value{value_} {} - ~IGBPQueryResponseParcel() override = default; - -protected: - void SerializeData() override { - Write(value); - } - -private: - u32_le value; + const u32 magic = 2; + const u32 process_id = 1; + const u32 id; + INSERT_PADDING_WORDS(3); + std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'}; + INSERT_PADDING_WORDS(2); }; +static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size"); class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { public: - explicit IHOSBinderDriver(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) - : ServiceFramework{system_, "IHOSBinderDriver"}, nv_flinger(nv_flinger_) { + explicit IHOSBinderDriver(Core::System& system_, NVFlinger::HosBinderDriverServer& server_) + : ServiceFramework{system_, "IHOSBinderDriver", ServiceThreadType::CreateNew}, + server(server_) { static const FunctionInfo functions[] = { {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"}, @@ -508,147 +85,16 @@ public: } private: - enum class TransactionId { - RequestBuffer = 1, - SetBufferCount = 2, - DequeueBuffer = 3, - DetachBuffer = 4, - DetachNextBuffer = 5, - AttachBuffer = 6, - QueueBuffer = 7, - CancelBuffer = 8, - Query = 9, - Connect = 10, - Disconnect = 11, - - AllocateBuffers = 13, - SetPreallocatedBuffer = 14, - - GetBufferHistory = 17 - }; - void TransactParcel(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 id = rp.Pop<u32>(); - const auto transaction = static_cast<TransactionId>(rp.Pop<u32>()); + const auto transaction = static_cast<android::TransactionId>(rp.Pop<u32>()); const u32 flags = rp.Pop<u32>(); LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, transaction, flags); - auto& buffer_queue = *nv_flinger.FindBufferQueue(id); - - switch (transaction) { - case TransactionId::Connect: { - IGBPConnectRequestParcel request{ctx.ReadBuffer()}; - IGBPConnectResponseParcel response{static_cast<u32>(DisplayResolution::UndockedWidth), - static_cast<u32>(DisplayResolution::UndockedHeight)}; - - buffer_queue.Connect(); - - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::SetPreallocatedBuffer: { - IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()}; - - buffer_queue.SetPreallocatedBuffer(request.data.slot, request.buffer_container.buffer); - - IGBPSetPreallocatedBufferResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::DequeueBuffer: { - IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; - const u32 width{request.data.width}; - const u32 height{request.data.height}; - - do { - if (auto result = buffer_queue.DequeueBuffer(width, height); result) { - // Buffer is available - IGBPDequeueBufferResponseParcel response{result->first, *result->second}; - ctx.WriteBuffer(response.Serialize()); - break; - } - } while (buffer_queue.IsConnected()); - - break; - } - case TransactionId::RequestBuffer: { - IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()}; - - auto& buffer = buffer_queue.RequestBuffer(request.slot); - IGBPRequestBufferResponseParcel response{buffer}; - ctx.WriteBuffer(response.Serialize()); - - break; - } - case TransactionId::QueueBuffer: { - IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()}; - - buffer_queue.QueueBuffer(request.data.slot, request.data.transform, - request.data.GetCropRect(), request.data.swap_interval, - request.data.multi_fence); - - IGBPQueueBufferResponseParcel response{1280, 720}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::Query: { - IGBPQueryRequestParcel request{ctx.ReadBuffer()}; - - const u32 value = - buffer_queue.Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type)); - - IGBPQueryResponseParcel response{value}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::CancelBuffer: { - IGBPCancelBufferRequestParcel request{ctx.ReadBuffer()}; - - buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence); - - IGBPCancelBufferResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::Disconnect: { - LOG_WARNING(Service_VI, "(STUBBED) called, transaction=Disconnect"); - const auto buffer = ctx.ReadBuffer(); - - buffer_queue.Disconnect(); - - IGBPEmptyResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::DetachBuffer: { - const auto buffer = ctx.ReadBuffer(); - - IGBPEmptyResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::SetBufferCount: { - LOG_WARNING(Service_VI, "(STUBBED) called, transaction=SetBufferCount"); - [[maybe_unused]] const auto buffer = ctx.ReadBuffer(); - - IGBPEmptyResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::GetBufferHistory: { - LOG_WARNING(Service_VI, "(STUBBED) called, transaction=GetBufferHistory"); - [[maybe_unused]] const auto buffer = ctx.ReadBuffer(); - - IGBPEmptyResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - default: - ASSERT_MSG(false, "Unimplemented"); - } + server.TryGetProducer(id)->Transact(ctx, transaction, flags); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -674,13 +120,13 @@ private: LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); - // TODO(Subv): Find out what this actually is. IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(nv_flinger.FindBufferQueue(id)->GetBufferWaitEvent()); + rb.PushCopyObjects(server.TryGetProducer(id)->GetNativeHandle()); } - NVFlinger::NVFlinger& nv_flinger; +private: + NVFlinger::HosBinderDriverServer& server; }; class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { @@ -899,7 +345,7 @@ private: if (!layer_id) { LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -937,7 +383,40 @@ private: class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { public: - explicit IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); + IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_) + : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_}, + hos_binder_driver_server{hos_binder_driver_server_} { + + static const FunctionInfo functions[] = { + {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"}, + {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"}, + {102, &IApplicationDisplayService::GetManagerDisplayService, + "GetManagerDisplayService"}, + {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService, + "GetIndirectDisplayTransactionService"}, + {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"}, + {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"}, + {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"}, + {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"}, + {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"}, + {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"}, + {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"}, + {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"}, + {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"}, + {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"}, + {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"}, + {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"}, + {2450, &IApplicationDisplayService::GetIndirectLayerImageMap, + "GetIndirectLayerImageMap"}, + {2451, nullptr, "GetIndirectLayerImageCropMap"}, + {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo, + "GetIndirectLayerImageRequiredMemoryInfo"}, + {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"}, + {5203, nullptr, "GetDisplayVsyncEventForDebug"}, + }; + RegisterHandlers(functions); + } private: enum class ConvertedScaleMode : u64 { @@ -961,7 +440,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger); + rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server); } void GetSystemDisplayService(Kernel::HLERequestContext& ctx) { @@ -985,7 +464,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger); + rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server); } void OpenDisplay(Kernel::HLERequestContext& ctx) { @@ -1016,7 +495,7 @@ private: if (!display_id) { LOG_ERROR(Service_VI, "Display not found! display_name={}", name); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -1072,14 +551,14 @@ private: if (scaling_mode > NintendoScaleMode::PreserveAspectRatio) { LOG_ERROR(Service_VI, "Invalid scaling mode provided."); - rb.Push(ERR_OPERATION_FAILED); + rb.Push(ResultOperationFailed); return; } if (scaling_mode != NintendoScaleMode::ScaleToWindow && scaling_mode != NintendoScaleMode::PreserveAspectRatio) { LOG_ERROR(Service_VI, "Unsupported scaling mode supplied."); - rb.Push(ERR_UNSUPPORTED); + rb.Push(ResultNotSupported); return; } @@ -1089,7 +568,7 @@ private: void ListDisplays(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_VI, "(STUBBED) called"); - DisplayInfo display_info; + const DisplayInfo display_info; ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); @@ -1112,7 +591,7 @@ private: if (!display_id) { LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -1120,12 +599,12 @@ private: if (!buffer_queue_id) { LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } - NativeWindow native_window{*buffer_queue_id}; - const auto buffer_size = ctx.WriteBuffer(native_window.Serialize()); + const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}}; + const auto buffer_size = ctx.WriteBuffer(parcel.Serialize()); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); @@ -1158,7 +637,7 @@ private: if (!layer_id) { LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -1166,12 +645,12 @@ private: if (!buffer_queue_id) { LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } - NativeWindow native_window{*buffer_queue_id}; - const auto buffer_size = ctx.WriteBuffer(native_window.Serialize()); + const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}}; + const auto buffer_size = ctx.WriteBuffer(parcel.Serialize()); IPC::ResponseBuilder rb{ctx, 6}; rb.Push(ResultSuccess); @@ -1193,19 +672,23 @@ private: IPC::RequestParser rp{ctx}; const u64 display_id = rp.Pop<u64>(); - LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); + LOG_DEBUG(Service_VI, "called. display_id={}", display_id); const auto vsync_event = nv_flinger.FindVsyncEvent(display_id); - if (!vsync_event) { - LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); + if (vsync_event.Failed()) { + const auto result = vsync_event.Code(); + if (result == ResultNotFound) { + LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); + } + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(result); return; } IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(vsync_event); + rb.PushCopyObjects(*vsync_event); } void ConvertScalingMode(Kernel::HLERequestContext& ctx) { @@ -1282,44 +765,14 @@ private: return ConvertedScaleMode::PreserveAspectRatio; default: LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode); - return ERR_OPERATION_FAILED; + return ResultOperationFailed; } } NVFlinger::NVFlinger& nv_flinger; + NVFlinger::HosBinderDriverServer& hos_binder_driver_server; }; -IApplicationDisplayService::IApplicationDisplayService(Core::System& system_, - NVFlinger::NVFlinger& nv_flinger_) - : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_} { - static const FunctionInfo functions[] = { - {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"}, - {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"}, - {102, &IApplicationDisplayService::GetManagerDisplayService, "GetManagerDisplayService"}, - {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService, - "GetIndirectDisplayTransactionService"}, - {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"}, - {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"}, - {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"}, - {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"}, - {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"}, - {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"}, - {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"}, - {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"}, - {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"}, - {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"}, - {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"}, - {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"}, - {2450, &IApplicationDisplayService::GetIndirectLayerImageMap, "GetIndirectLayerImageMap"}, - {2451, nullptr, "GetIndirectLayerImageCropMap"}, - {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo, - "GetIndirectLayerImageRequiredMemoryInfo"}, - {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"}, - {5203, nullptr, "GetDisplayVsyncEventForDebug"}, - }; - RegisterHandlers(functions); -} - static bool IsValidServiceAccess(Permission permission, Policy policy) { if (permission == Permission::User) { return policy == Policy::User; @@ -1333,27 +786,33 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) { } void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system, - NVFlinger::NVFlinger& nv_flinger, Permission permission) { + NVFlinger::NVFlinger& nv_flinger, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server, + Permission permission) { IPC::RequestParser rp{ctx}; const auto policy = rp.PopEnum<Policy>(); if (!IsValidServiceAccess(permission, policy)) { LOG_ERROR(Service_VI, "Permission denied for policy {}", policy); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_PERMISSION_DENIED); + rb.Push(ResultPermissionDenied); return; } IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger); + rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger, hos_binder_driver_server); } void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system, - NVFlinger::NVFlinger& nv_flinger) { - std::make_shared<VI_M>(system, nv_flinger)->InstallAsService(service_manager); - std::make_shared<VI_S>(system, nv_flinger)->InstallAsService(service_manager); - std::make_shared<VI_U>(system, nv_flinger)->InstallAsService(service_manager); + NVFlinger::NVFlinger& nv_flinger, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server) { + std::make_shared<VI_M>(system, nv_flinger, hos_binder_driver_server) + ->InstallAsService(service_manager); + std::make_shared<VI_S>(system, nv_flinger, hos_binder_driver_server) + ->InstallAsService(service_manager); + std::make_shared<VI_U>(system, nv_flinger, hos_binder_driver_server) + ->InstallAsService(service_manager); } } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h index 2fd7f8e61..fc2d717e7 100644 --- a/src/core/hle/service/vi/vi.h +++ b/src/core/hle/service/vi/vi.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -15,8 +14,9 @@ class HLERequestContext; } namespace Service::NVFlinger { +class HosBinderDriverServer; class NVFlinger; -} +} // namespace Service::NVFlinger namespace Service::SM { class ServiceManager; @@ -47,11 +47,14 @@ enum class Policy { namespace detail { void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system, - NVFlinger::NVFlinger& nv_flinger, Permission permission); + NVFlinger::NVFlinger& nv_flinger, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server, + Permission permission); } // namespace detail /// Registers all VI services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system, - NVFlinger::NVFlinger& nv_flinger); + NVFlinger::NVFlinger& nv_flinger, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server); } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp index 87db1c416..1ab7fe4ab 100644 --- a/src/core/hle/service/vi/vi_m.cpp +++ b/src/core/hle/service/vi/vi_m.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/service/vi/vi.h" @@ -8,8 +7,10 @@ namespace Service::VI { -VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) - : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_} { +VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_) + : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{ + hos_binder_driver_server_} { static const FunctionInfo functions[] = { {2, &VI_M::GetDisplayService, "GetDisplayService"}, {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, @@ -22,7 +23,8 @@ VI_M::~VI_M() = default; void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_VI, "called"); - detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::Manager); + detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server, + Permission::Manager); } } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h index d79c41beb..3bf76d439 100644 --- a/src/core/hle/service/vi/vi_m.h +++ b/src/core/hle/service/vi/vi_m.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -15,20 +14,23 @@ class HLERequestContext; } namespace Service::NVFlinger { +class HosBinderDriverServer; class NVFlinger; -} +} // namespace Service::NVFlinger namespace Service::VI { class VI_M final : public ServiceFramework<VI_M> { public: - explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); + explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_); ~VI_M() override; private: void GetDisplayService(Kernel::HLERequestContext& ctx); NVFlinger::NVFlinger& nv_flinger; + NVFlinger::HosBinderDriverServer& hos_binder_driver_server; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_results.h b/src/core/hle/service/vi/vi_results.h new file mode 100644 index 000000000..a46c247d2 --- /dev/null +++ b/src/core/hle/service/vi/vi_results.h @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/result.h" + +namespace Service::VI { + +constexpr Result ResultOperationFailed{ErrorModule::VI, 1}; +constexpr Result ResultPermissionDenied{ErrorModule::VI, 5}; +constexpr Result ResultNotSupported{ErrorModule::VI, 6}; +constexpr Result ResultNotFound{ErrorModule::VI, 7}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp index 5cd22f7df..fd799dac1 100644 --- a/src/core/hle/service/vi/vi_s.cpp +++ b/src/core/hle/service/vi/vi_s.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/service/vi/vi.h" @@ -8,8 +7,10 @@ namespace Service::VI { -VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) - : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_} { +VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_) + : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{ + hos_binder_driver_server_} { static const FunctionInfo functions[] = { {1, &VI_S::GetDisplayService, "GetDisplayService"}, {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, @@ -22,7 +23,8 @@ VI_S::~VI_S() = default; void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_VI, "called"); - detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::System); + detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server, + Permission::System); } } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h index 5f1f8f290..97503ac7f 100644 --- a/src/core/hle/service/vi/vi_s.h +++ b/src/core/hle/service/vi/vi_s.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -15,20 +14,23 @@ class HLERequestContext; } namespace Service::NVFlinger { +class HosBinderDriverServer; class NVFlinger; -} +} // namespace Service::NVFlinger namespace Service::VI { class VI_S final : public ServiceFramework<VI_S> { public: - explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); + explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_); ~VI_S() override; private: void GetDisplayService(Kernel::HLERequestContext& ctx); NVFlinger::NVFlinger& nv_flinger; + NVFlinger::HosBinderDriverServer& hos_binder_driver_server; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp index 0079d51f0..6cc54bd13 100644 --- a/src/core/hle/service/vi/vi_u.cpp +++ b/src/core/hle/service/vi/vi_u.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/service/vi/vi.h" @@ -8,8 +7,10 @@ namespace Service::VI { -VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) - : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_} { +VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_) + : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{ + hos_binder_driver_server_} { static const FunctionInfo functions[] = { {0, &VI_U::GetDisplayService, "GetDisplayService"}, {1, nullptr, "GetDisplayServiceWithProxyNameExchange"}, @@ -22,7 +23,8 @@ VI_U::~VI_U() = default; void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_VI, "called"); - detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::User); + detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server, + Permission::User); } } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h index 8e3885c73..797941bd7 100644 --- a/src/core/hle/service/vi/vi_u.h +++ b/src/core/hle/service/vi/vi_u.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -15,20 +14,23 @@ class HLERequestContext; } namespace Service::NVFlinger { +class HosBinderDriverServer; class NVFlinger; -} +} // namespace Service::NVFlinger namespace Service::VI { class VI_U final : public ServiceFramework<VI_U> { public: - explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); + explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_); ~VI_U() override; private: void GetDisplayService(Kernel::HLERequestContext& ctx); NVFlinger::NVFlinger& nv_flinger; + NVFlinger::HosBinderDriverServer& hos_binder_driver_server; }; } // namespace Service::VI diff --git a/src/core/hle/service/wlan/wlan.cpp b/src/core/hle/service/wlan/wlan.cpp index f10b8c853..226e3034c 100644 --- a/src/core/hle/service/wlan/wlan.cpp +++ b/src/core/hle/service/wlan/wlan.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/wlan/wlan.h b/src/core/hle/service/wlan/wlan.h index 3899eedbb..535c3bf0d 100644 --- a/src/core/hle/service/wlan/wlan.h +++ b/src/core/hle/service/wlan/wlan.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once |