From e0171844450b98a13ebf1e414cd8b45c9b6223be Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 13 Feb 2018 19:05:06 -0500 Subject: [PATCH 01/28] hid: Stub GetVibrationDeviceInfo and SendVibrationValues. --- src/core/hle/service/hid/hid.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index d757d2eae7..3f1c185059 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -186,7 +186,9 @@ public: {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, {124, nullptr, "SetNpadJoyAssignmentModeDual"}, {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, + {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, {203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"}, + {206, &Hid::SendVibrationValues, "SendVibrationValues"}, }; RegisterHandlers(functions); @@ -272,12 +274,25 @@ private: LOG_WARNING(Service_HID, "(STUBBED) called"); } + void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(0); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface(); LOG_DEBUG(Service_HID, "called"); } + + void SendVibrationValues(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } }; void ReloadInputDevices() {} From fa58d95027fc528b593794ac35684ba52f28afc7 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 14 Feb 2018 21:43:11 -0500 Subject: [PATCH 02/28] log: Add logging category for NS services. --- src/common/logging/backend.cpp | 1 + src/common/logging/log.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 2bbc5bb164..8274b23886 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -40,6 +40,7 @@ namespace Log { SUB(Service, HID) \ SUB(Service, LM) \ SUB(Service, NIFM) \ + SUB(Service, NS) \ SUB(Service, NVDRV) \ SUB(Service, PCTL) \ SUB(Service, SET) \ diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 0d79b84988..69472ef1ac 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -57,6 +57,7 @@ enum class Class : ClassType { Service_HID, ///< The HID (Human interface device) service Service_LM, ///< The LM (Logger) service Service_NIFM, ///< The NIFM (Network interface) service + Service_NS, ///< The NS services Service_NVDRV, ///< The NVDRV (Nvidia driver) service Service_PCTL, ///< The PCTL (Parental control) service Service_SET, ///< The SET (Settings) service From 42c062c620b8aff1bee000691f997b0cf0388d15 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 14 Feb 2018 22:22:41 -0500 Subject: [PATCH 03/28] pl_u: Implement basic shared font loading from RAM dump. --- src/core/CMakeLists.txt | 4 ++ src/core/hle/service/ns/ns.cpp | 16 +++++ src/core/hle/service/ns/ns.h | 16 +++++ src/core/hle/service/ns/pl_u.cpp | 111 +++++++++++++++++++++++++++++++ src/core/hle/service/ns/pl_u.h | 33 +++++++++ src/core/hle/service/service.cpp | 2 + 6 files changed, 182 insertions(+) create mode 100644 src/core/hle/service/ns/ns.cpp create mode 100644 src/core/hle/service/ns/ns.h create mode 100644 src/core/hle/service/ns/pl_u.cpp create mode 100644 src/core/hle/service/ns/pl_u.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index fc6cb67c7f..ce68194c54 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -124,6 +124,10 @@ add_library(core STATIC hle/service/nifm/nifm_s.h hle/service/nifm/nifm_u.cpp hle/service/nifm/nifm_u.h + hle/service/ns/ns.cpp + hle/service/ns/ns.h + hle/service/ns/pl_u.cpp + hle/service/ns/pl_u.h hle/service/nvdrv/devices/nvdevice.h hle/service/nvdrv/devices/nvdisp_disp0.cpp hle/service/nvdrv/devices/nvdisp_disp0.h diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp new file mode 100644 index 0000000000..45681c50fb --- /dev/null +++ b/src/core/hle/service/ns/ns.cpp @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/ns/ns.h" +#include "core/hle/service/ns/pl_u.h" + +namespace Service { +namespace NS { + +void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared()->InstallAsService(service_manager); +} + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h new file mode 100644 index 0000000000..a4b7e3dedf --- /dev/null +++ b/src/core/hle/service/ns/ns.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace NS { + +/// Registers all NS services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp new file mode 100644 index 0000000000..cc9d03a7c0 --- /dev/null +++ b/src/core/hle/service/ns/pl_u.cpp @@ -0,0 +1,111 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/common_paths.h" +#include "common/file_util.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/ns/pl_u.h" + +namespace Service { +namespace NS { + +struct FontRegion { + u32 offset; + u32 size; +}; + +// The below data is specific to shared font data dumped from Switch on f/w 2.2 +// Virtual address and offsets/sizes likely will vary by dump +static constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL}; +static constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000}; +static constexpr std::array SHARED_FONT_REGIONS{ + FontRegion{0x00000008, 0x001fe764}, FontRegion{0x001fe774, 0x00773e58}, + FontRegion{0x009725d4, 0x0001aca8}, FontRegion{0x0098d284, 0x00369cec}, + FontRegion{0x00cf6f78, 0x0039b858}, FontRegion{0x010927d8, 0x00019e80}, +}; + +enum class LoadState : u32 { + Loading = 0, + Done = 1, +}; + +PL_U::PL_U() : ServiceFramework("pl:u") { + static const FunctionInfo functions[] = { + {1, &PL_U::GetLoadState, "GetLoadState"}, + {2, &PL_U::GetSize, "GetSize"}, + {3, &PL_U::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"}, + {4, &PL_U::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}}; + RegisterHandlers(functions); + + // Attempt to load shared font data from disk + const std::string filepath{FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT}; + FileUtil::CreateFullPath(filepath); // Create path if not already created + FileUtil::IOFile file(filepath, "rb"); + + if (file.IsOpen()) { + // Read shared font data + ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE); + shared_font = std::make_shared>(static_cast(file.GetSize())); + file.ReadBytes(shared_font->data(), shared_font->size()); + } else { + LOG_WARNING(Service_NS, "Unable to load shared font: %s", filepath.c_str()); + } +} + +void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 font_id{rp.Pop()}; + + LOG_DEBUG(Service_NS, "called, font_id=%d", font_id); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast(LoadState::Done)); +} + +void PL_U::GetSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 font_id{rp.Pop()}; + + LOG_DEBUG(Service_NS, "called, font_id=%d", font_id); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(SHARED_FONT_REGIONS[font_id].size); +} + +void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 font_id{rp.Pop()}; + + LOG_DEBUG(Service_NS, "called, font_id=%d", font_id); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(SHARED_FONT_REGIONS[font_id].offset); +} + +void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { + if (shared_font != nullptr) { + // TODO(bunnei): This is a less-than-ideal solution to load a RAM dump of the Switch shared + // font data. This (likely) relies on exact address, size, and offsets from the original + // dump. In the future, we need to replace this with a more robust solution. + + // Map backing memory for the font data + Kernel::g_current_process->vm_manager.MapMemoryBlock(SHARED_FONT_MEM_VADDR, shared_font, 0, + SHARED_FONT_MEM_SIZE, + Kernel::MemoryState::Shared); + + // Create shared font memory object + shared_font_mem = Kernel::SharedMemory::Create( + Kernel::g_current_process, SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, + Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, + "PL_U:shared_font_mem"); + } + + LOG_DEBUG(Service_NS, "called"); + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(shared_font_mem); +} + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/pl_u.h new file mode 100644 index 0000000000..7a4766338a --- /dev/null +++ b/src/core/hle/service/ns/pl_u.h @@ -0,0 +1,33 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace NS { + +class PL_U final : public ServiceFramework { +public: + PL_U(); + ~PL_U() = default; + +private: + void GetLoadState(Kernel::HLERequestContext& ctx); + void GetSize(Kernel::HLERequestContext& ctx); + void GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx); + void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx); + + /// Handle to shared memory region designated for a shared font + Kernel::SharedPtr shared_font_mem; + + /// Backing memory for the shared font data + std::shared_ptr> shared_font; +}; + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 1dd04a12f4..5e6d837290 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -23,6 +23,7 @@ #include "core/hle/service/hid/hid.h" #include "core/hle/service/lm/lm.h" #include "core/hle/service/nifm/nifm.h" +#include "core/hle/service/ns/ns.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/pctl/pctl.h" #include "core/hle/service/service.h" @@ -182,6 +183,7 @@ void Init() { HID::InstallInterfaces(*SM::g_service_manager); LM::InstallInterfaces(*SM::g_service_manager); NIFM::InstallInterfaces(*SM::g_service_manager); + NS::InstallInterfaces(*SM::g_service_manager); Nvidia::InstallInterfaces(*SM::g_service_manager); PCTL::InstallInterfaces(*SM::g_service_manager); Sockets::InstallInterfaces(*SM::g_service_manager); From df008a159b7c1fb790f83e29d71b98588ed45559 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 14 Feb 2018 22:24:06 -0500 Subject: [PATCH 04/28] shared_memory: Remove some checks. --- src/core/hle/kernel/shared_memory.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 835fc710b7..d4505061ef 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -111,13 +111,6 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi return ERR_INVALID_COMBINATION; } - // Heap-backed memory blocks can not be mapped with other_permissions = DontCare - if (base_address != 0 && other_permissions == MemoryPermission::DontCare) { - LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, permissions don't match", - GetObjectId(), address, name.c_str()); - return ERR_INVALID_COMBINATION; - } - // Error out if the provided permissions are not compatible with what the creator process needs. if (other_permissions != MemoryPermission::DontCare && static_cast(this->permissions) & ~static_cast(other_permissions)) { @@ -126,12 +119,6 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi return ERR_WRONG_PERMISSION; } - // TODO(Subv): Check for the Shared Device Mem flag in the creator process. - /*if (was_created_with_shared_device_mem && address != 0) { - return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); - }*/ - // TODO(Subv): The same process that created a SharedMemory object // can not map it in its own address space unless it was created with addr=0, result 0xD900182C. From 8dee5663b3b2ae5dd1b5a4b9eeacf030caa0de9a Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 13 Feb 2018 22:12:46 -0500 Subject: [PATCH 05/28] Vi: Properly write the BufferProducerFence object in the DequeueBuffer response parcel. --- src/core/hle/service/nvdrv/nvdrv.h | 7 ++++++ src/core/hle/service/vi/vi.cpp | 39 ++++++++++++++++-------------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index e446446241..6a55ff96de 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -17,6 +17,13 @@ namespace Devices { class nvdevice; } +struct IoctlFence { + u32 id; + u32 value; +}; + +static_assert(sizeof(IoctlFence) == 8, "IoctlFence has wrong size"); + class Module final { public: Module(); diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index ff5005f71f..9394a06a79 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -8,6 +8,7 @@ #include "common/scope_exit.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvflinger/buffer_queue.h" #include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi_m.h" @@ -86,6 +87,15 @@ public: write_index = Common::AlignUp(write_index, 4); } + template + void WriteObject(const T& val) { + u32_le size = static_cast(sizeof(val)); + Write(size); + // TODO(Subv): Support file descriptors. + Write(0); // Fd count. + Write(val); + } + void Deserialize() { Header header{}; std::memcpy(&header, buffer.data(), sizeof(Header)); @@ -262,10 +272,11 @@ public: Data data; }; -// TODO(bunnei): Remove this. When set to 1, games will think a fence is valid and boot further. -// This will break libnx and potentially other apps that more stringently check this. This is here -// purely as a convenience, and should go away once we implement fences. -static constexpr u32 FENCE_HACK = 0; +struct BufferProducerFence { + u32 is_valid; + std::array fences; +}; +static_assert(sizeof(BufferProducerFence) == 36, "BufferProducerFence has wrong size"); class IGBPDequeueBufferResponseParcel : public Parcel { public: @@ -274,20 +285,12 @@ public: protected: void SerializeData() override { - // TODO(bunnei): Find out what this all means. Writing anything non-zero here breaks libnx. - Write(0); - Write(FENCE_HACK); - Write(0); - Write(0); - Write(0); - Write(0); - Write(0); - Write(0); - Write(0); - Write(0); - Write(0); - Write(0); - Write(0); + // TODO(Subv): Find out how this Fence is used. + BufferProducerFence fence = {}; + + Write(slot); + WriteObject(fence); + Write(0); } u32_le slot; From b78ffc4abf57a85a0caaaeb7bf6a51901c8e27b4 Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 13 Feb 2018 22:23:53 -0500 Subject: [PATCH 06/28] Vi: Don't write the IGBPBuffer in the IGBPRequestBufferResponseParcel. --- src/core/hle/service/vi/vi.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 9394a06a79..439af318c5 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -319,11 +319,9 @@ public: protected: void SerializeData() override { - // TODO(bunnei): Find out what this all means. Writing anything non-zero here breaks libnx. + // 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(0); - Write(FENCE_HACK); - Write(0); - Write(buffer); Write(0); } From d18446f63af86cf0dd55a42e8768b97665a18732 Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 13 Feb 2018 22:51:24 -0500 Subject: [PATCH 07/28] Vi: Added a missing u32 in the DequeueBuffer response parcel. --- src/core/hle/service/vi/vi.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 439af318c5..7a71a4b3dc 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -289,6 +289,7 @@ protected: BufferProducerFence fence = {}; Write(slot); + Write(1); WriteObject(fence); Write(0); } From 35d0d06885c13a984c9dd648771107fbdeb81fa7 Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 13 Feb 2018 23:03:02 -0500 Subject: [PATCH 08/28] Vi: Mark the fences as valid in the DequeueBuffer response parcel. --- src/core/hle/service/vi/vi.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 7a71a4b3dc..048fe3a6f7 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -287,6 +287,9 @@ protected: void SerializeData() override { // TODO(Subv): Find out how this Fence is used. BufferProducerFence fence = {}; + fence.is_valid = 1; + fence.fences[0].id = 0; + fence.fences[0].value = 0; Write(slot); Write(1); From 7a1917e0fde95b6da6a4d95111bb0462c072cd5c Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 13 Feb 2018 23:14:11 -0500 Subject: [PATCH 09/28] nvhost-ctrl: Stub NVHOST_IOCTL_CTRL_EVENT_WAIT. --- src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp | 14 ++++++++++++++ src/core/hle/service/nvdrv/devices/nvhost_ctrl.h | 11 +++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index ee99ab2807..45711d6863 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -17,6 +17,8 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector& input, std::vector< switch (static_cast(command.raw)) { case IoctlCommand::IocGetConfigCommand: return NvOsGetConfigU32(input, output); + case IoctlCommand::IocCtrlEventWaitCommand: + return IocCtrlEventWait(input, output); } UNIMPLEMENTED(); return 0; @@ -45,6 +47,18 @@ u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector& input, std::vector& return 0; } +u32 nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector& output) { + IocCtrlEventWaitParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, syncpt_id=%u threshold=%u timeout=%d", + params.syncpt_id, params.threshold, params.timeout); + + // TODO(Subv): Implement actual syncpt waiting. + params.value = 0; + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; +} + } // namespace Devices } // namespace Nvidia } // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index fd02a5e45f..0ca01aa6d2 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -31,6 +31,7 @@ private: IocModuleRegRDWRCommand = 0xC008010E, IocSyncptWaitexCommand = 0xC0100019, IocSyncptReadMaxCommand = 0xC008001A, + IocCtrlEventWaitCommand = 0xC010001D, IocGetConfigCommand = 0xC183001B, }; @@ -41,7 +42,17 @@ private: }; static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); + struct IocCtrlEventWaitParams { + u32_le syncpt_id; + u32_le threshold; + s32_le timeout; + u32_le value; + }; + static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size"); + u32 NvOsGetConfigU32(const std::vector& input, std::vector& output); + + u32 IocCtrlEventWait(const std::vector& input, std::vector& output); }; } // namespace Devices From 6797d4a907f8c2022ef3dba10aac5d2d7667135a Mon Sep 17 00:00:00 2001 From: mailwl Date: Thu, 15 Feb 2018 17:22:11 +0300 Subject: [PATCH 10/28] Service/hid: stub some functions --- src/core/hle/kernel/hle_ipc.cpp | 51 +++++++++++++++++++++++++ src/core/hle/kernel/hle_ipc.h | 2 + src/core/hle/service/audio/audren_u.cpp | 3 +- src/core/hle/service/hid/hid.cpp | 43 +++++++++++++++++++++ 4 files changed, 98 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 6d16f71a7c..25ba26f189 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -254,4 +254,55 @@ size_t HLERequestContext::GetWriteBufferSize() const { return is_buffer_b ? BufferDescriptorB()[0].Size() : BufferDescriptorC()[0].Size(); } +std::string HLERequestContext::Description() const { + if (!command_header) { + return "No command header available"; + } + std::ostringstream s; + s << "IPC::CommandHeader: Type:" << static_cast(command_header->type.Value()); + s << ", X(Pointer):" << command_header->num_buf_x_descriptors; + if (command_header->num_buf_x_descriptors) { + s << '['; + for (u64 i = 0; i < command_header->num_buf_x_descriptors; ++i) { + s << "0x" << std::hex << BufferDescriptorX()[i].Size(); + if (i < command_header->num_buf_x_descriptors - 1) + s << ", "; + } + s << ']'; + } + s << ", A(Send):" << command_header->num_buf_a_descriptors; + if (command_header->num_buf_a_descriptors) { + s << '['; + for (u64 i = 0; i < command_header->num_buf_a_descriptors; ++i) { + s << "0x" << std::hex << BufferDescriptorA()[i].Size(); + if (i < command_header->num_buf_a_descriptors - 1) + s << ", "; + } + s << ']'; + } + s << ", B(Receive):" << command_header->num_buf_b_descriptors; + if (command_header->num_buf_b_descriptors) { + s << '['; + for (u64 i = 0; i < command_header->num_buf_b_descriptors; ++i) { + s << "0x" << std::hex << BufferDescriptorB()[i].Size(); + if (i < command_header->num_buf_b_descriptors - 1) + s << ", "; + } + s << ']'; + } + s << ", C(ReceiveList):" << BufferDescriptorC().size(); + if (!BufferDescriptorC().empty()) { + s << '['; + for (u64 i = 0; i < BufferDescriptorC().size(); ++i) { + s << "0x" << std::hex << BufferDescriptorC()[i].Size(); + if (i < BufferDescriptorC().size() - 1) + s << ", "; + } + s << ']'; + } + s << ", data_size:" << command_header->data_size.Value(); + + return s.str(); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 81e3489c8d..b5631b7737 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -202,6 +202,8 @@ public: return domain_objects.size(); } + std::string Description() const; + private: std::array cmd_buf; SharedPtr server_session; diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 20306c6cfd..4efc789acb 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -53,7 +53,8 @@ private: } void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { - AudioRendererResponseData response_data = {0}; + LOG_DEBUG(Service_Audio, "%s", ctx.Description().c_str()); + AudioRendererResponseData response_data{}; response_data.section_0_size = response_data.state_entries.size() * sizeof(AudioRendererStateEntry); diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 3f1c185059..dacd1862df 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -176,7 +176,10 @@ public: {0, &Hid::CreateAppletResource, "CreateAppletResource"}, {1, &Hid::ActivateDebugPad, "ActivateDebugPad"}, {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"}, + {21, &Hid::ActivateMouse, "ActivateMouse"}, + {31, &Hid::ActivateKeyboard, "ActivateKeyboard"}, {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, + {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, {103, &Hid::ActivateNpad, "ActivateNpad"}, @@ -184,9 +187,13 @@ public: "AcquireNpadStyleSetUpdateEventHandle"}, {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, + {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, + "SetNpadJoyAssignmentModeSingleByDefault"}, {124, nullptr, "SetNpadJoyAssignmentModeDual"}, {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, + {201, &Hid::SendVibrationValue, "SendVibrationValue"}, + {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, {203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"}, {206, &Hid::SendVibrationValues, "SendVibrationValues"}, }; @@ -224,12 +231,30 @@ private: LOG_WARNING(Service_HID, "(STUBBED) called"); } + void ActivateMouse(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void ActivateKeyboard(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + void StartSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_HID, "(STUBBED) called"); } + void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -268,6 +293,24 @@ private: LOG_WARNING(Service_HID, "(STUBBED) called"); } + void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SendVibrationValue(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void GetActualVibrationValue(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); From 947831ff1e09fa1892e0e584dd57360ebf4e2f4d Mon Sep 17 00:00:00 2001 From: N00byKing Date: Fri, 16 Feb 2018 21:37:35 +0100 Subject: [PATCH 11/28] Use Docker for Build Target clang-format for travis. This uses the (apparently) more stable Ubuntu Repo instead of the LLVM one. --- .travis.yml | 13 ++++--------- .travis/clang-format/build.sh | 3 +++ .travis/clang-format/deps.sh | 3 +++ .travis/clang-format/docker.sh | 8 ++++++++ 4 files changed, 18 insertions(+), 9 deletions(-) create mode 100755 .travis/clang-format/build.sh create mode 100755 .travis/clang-format/deps.sh create mode 100755 .travis/clang-format/docker.sh diff --git a/.travis.yml b/.travis.yml index e2742b5fe2..044708b1d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,16 +3,11 @@ matrix: include: - os: linux env: NAME="clang-format" + sudo: required dist: trusty - addons: - apt: - sources: - - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-6.0 main' - key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' - - sourceline: 'deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main' - packages: - - clang-format-6.0 - script: "./.travis/clang-format/script.sh" + services: docker + install: "./.travis/clang-format/deps.sh" + script: "./.travis/clang-format/build.sh" - os: linux env: NAME="linux build" sudo: required diff --git a/.travis/clang-format/build.sh b/.travis/clang-format/build.sh new file mode 100755 index 0000000000..ad8960950d --- /dev/null +++ b/.travis/clang-format/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash -ex + +docker run -v $(pwd):/yuzu ubuntu:18.04 /bin/bash /yuzu/.travis/clang-format/docker.sh diff --git a/.travis/clang-format/deps.sh b/.travis/clang-format/deps.sh new file mode 100755 index 0000000000..540bb934a8 --- /dev/null +++ b/.travis/clang-format/deps.sh @@ -0,0 +1,3 @@ +#!/bin/sh -ex + +docker pull ubuntu:18.04 diff --git a/.travis/clang-format/docker.sh b/.travis/clang-format/docker.sh new file mode 100755 index 0000000000..05047e9b86 --- /dev/null +++ b/.travis/clang-format/docker.sh @@ -0,0 +1,8 @@ +#!/bin/bash -ex + +apt-get update +apt-get install -y clang-format-6.0 + +# Run clang-format +cd /yuzu +./.travis/clang-format/script.sh From bd6432f1ffdb90c3381478c47cffacd66c4c8a93 Mon Sep 17 00:00:00 2001 From: FernandoS27 Date: Sat, 17 Feb 2018 14:57:32 -0400 Subject: [PATCH 12/28] updated dynarmic --- externals/dynarmic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/dynarmic b/externals/dynarmic index d7323d6799..e585e1d49e 160000 --- a/externals/dynarmic +++ b/externals/dynarmic @@ -1 +1 @@ -Subproject commit d7323d6799f0845b8c3214d624efce7a3094a657 +Subproject commit e585e1d49ed65c31edd567510e00508d42decb1c From 1b64160d835d23141cb150cb1ea9b2ab62b5c835 Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 17 Feb 2018 13:59:45 -0500 Subject: [PATCH 13/28] Vi: Always write the IGBPBuffer in the RequestBuffer response parcel. This may break libnx homebrew due to a bug in libnx but is required by official games since they always assume that the buffer will be there. --- src/core/hle/service/vi/vi.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 048fe3a6f7..e5f196158c 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -325,7 +325,8 @@ 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(0); + Write(1); + WriteObject(buffer); Write(0); } From 2662de6e52344e082bc5855ea0e3e791588862c6 Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 17 Feb 2018 14:00:30 -0500 Subject: [PATCH 14/28] Vi: Mark all fences as NO_FENCE in the DequeueBuffer response parcel. --- src/core/hle/service/vi/vi.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index e5f196158c..1afd5a4fb9 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -288,8 +288,8 @@ protected: // TODO(Subv): Find out how this Fence is used. BufferProducerFence fence = {}; fence.is_valid = 1; - fence.fences[0].id = 0; - fence.fences[0].value = 0; + for (auto& fence_ : fence.fences) + fence_.id = -1; Write(slot); Write(1); From d7583324259c0c99d9f4a545751dc109229c6f05 Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 17 Feb 2018 13:54:59 -0500 Subject: [PATCH 15/28] Parcel: Ensure we don't read past the end of the parcels in Vi. --- src/core/hle/service/vi/vi.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 1afd5a4fb9..0aa621dfe2 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -39,6 +39,7 @@ public: template T Read() { + ASSERT(read_index + sizeof(T) <= buffer.size()); T val; std::memcpy(&val, buffer.data() + read_index, sizeof(T)); read_index += sizeof(T); @@ -48,6 +49,7 @@ public: template T ReadUnaligned() { + ASSERT(read_index + sizeof(T) <= buffer.size()); T val; std::memcpy(&val, buffer.data() + read_index, sizeof(T)); read_index += sizeof(T); @@ -55,6 +57,7 @@ public: } std::vector ReadBlock(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 data(begin, end); @@ -97,6 +100,8 @@ public: } void Deserialize() { + ASSERT(buffer.size() > sizeof(Header)); + Header header{}; std::memcpy(&header, buffer.data(), sizeof(Header)); From 416f692f6e1b8a4c838977b0cf6d9ae2ecbd15d6 Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 17 Feb 2018 13:56:21 -0500 Subject: [PATCH 16/28] nvmap: Make IocFromId return the same existing handle instead of creating a new one. Games like Puyo Puyo Tetris and BOTW seem to depend on the buffer always having the same handle --- src/core/hle/service/nvdrv/devices/nvmap.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index cd8c0c6055..b3842eb4ce 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -103,11 +103,8 @@ u32 nvmap::IocFromId(const std::vector& input, std::vector& output) { [&](const auto& entry) { return entry.second->id == params.id; }); ASSERT(itr != handles.end()); - // Make a new handle for the object - u32 handle = next_handle++; - handles[handle] = itr->second; - - params.handle = handle; + // Return the existing handle instead of creating a new one. + params.handle = itr->first; std::memcpy(output.data(), ¶ms, sizeof(params)); return 0; From b36ce74d181eaf854bb42855c6de5beee37e1a1b Mon Sep 17 00:00:00 2001 From: N00byKing Date: Sun, 18 Feb 2018 01:01:24 +0100 Subject: [PATCH 17/28] Update build.sh --- .travis/clang-format/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/clang-format/build.sh b/.travis/clang-format/build.sh index ad8960950d..3707be3c9f 100755 --- a/.travis/clang-format/build.sh +++ b/.travis/clang-format/build.sh @@ -1,3 +1,3 @@ #!/bin/bash -ex -docker run -v $(pwd):/yuzu ubuntu:18.04 /bin/bash /yuzu/.travis/clang-format/docker.sh +docker run -v $(pwd):/yuzu ubuntu:18.04 /bin/bash -ex /yuzu/.travis/clang-format/docker.sh From 94ee8fc97b6e3373d801d70efdd1b604ad03b85e Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 18 Feb 2018 13:22:19 -0500 Subject: [PATCH 18/28] Kernel/IPC: Add a small delay after each SyncRequest to prevent thread starvation. Ported from citra PR #3091 The delay specified here is from a Nintendo 3DS, and should be measured in a Nintendo Switch. This change is enough to prevent Puyo Puyo Tetris's main thread starvation. --- src/core/hle/kernel/server_session.cpp | 95 +++++++++++++++----------- src/core/hle/kernel/server_session.h | 14 ++-- src/core/hle/kernel/thread.cpp | 1 + src/core/hle/kernel/thread.h | 1 + 4 files changed, 62 insertions(+), 49 deletions(-) diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 54481f7f1f..5608418c3c 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -57,6 +57,33 @@ void ServerSession::Acquire(Thread* thread) { pending_requesting_threads.pop_back(); } +ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { + auto& domain_message_header = context.GetDomainMessageHeader(); + if (domain_message_header) { + // If there is a DomainMessageHeader, then this is CommandType "Request" + const u32 object_id{context.GetDomainMessageHeader()->object_id}; + switch (domain_message_header->command) { + case IPC::DomainMessageHeader::CommandType::SendMessage: + return domain_request_handlers[object_id - 1]->HandleSyncRequest(context); + + case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { + LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id); + + domain_request_handlers[object_id - 1] = nullptr; + + IPC::ResponseBuilder rb{context, 2}; + rb.Push(RESULT_SUCCESS); + return RESULT_SUCCESS; + } + } + + LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value()); + ASSERT(false); + } + + return RESULT_SUCCESS; +} + ResultCode ServerSession::HandleSyncRequest(SharedPtr thread) { // The ServerSession received a sync request, this means that there's new data available // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or @@ -67,46 +94,39 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr thread) { context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, Kernel::g_handle_table); - // If the session has been converted to a domain, handle the doomain request + ResultCode result = RESULT_SUCCESS; + // If the session has been converted to a domain, handle the domain request if (IsDomain()) { - auto& domain_message_header = context.GetDomainMessageHeader(); - if (domain_message_header) { - // If there is a DomainMessageHeader, then this is CommandType "Request" - const u32 object_id{context.GetDomainMessageHeader()->object_id}; - switch (domain_message_header->command) { - case IPC::DomainMessageHeader::CommandType::SendMessage: - return domain_request_handlers[object_id - 1]->HandleSyncRequest(context); - - case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { - LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id); - - domain_request_handlers[object_id - 1] = nullptr; - - IPC::ResponseBuilder rb{context, 2}; - rb.Push(RESULT_SUCCESS); - return RESULT_SUCCESS; - } - } - - LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value()); - ASSERT(false); - } + result = HandleDomainSyncRequest(context); // If there is no domain header, the regular session handler is used + } else if (hle_handler != nullptr) { + // If this ServerSession has an associated HLE handler, forward the request to it. + result = hle_handler->HandleSyncRequest(context); } - // If this ServerSession has an associated HLE handler, forward the request to it. - ResultCode result{RESULT_SUCCESS}; - if (hle_handler != nullptr) { - // Attempt to translate the incoming request's command buffer. - ResultCode translate_result = TranslateHLERequest(this); - if (translate_result.IsError()) - return translate_result; + if (thread->status == THREADSTATUS_RUNNING) { + // Put the thread to sleep until the server replies, it will be awoken in + // svcReplyAndReceive for LLE servers. + thread->status = THREADSTATUS_WAIT_IPC; - result = hle_handler->HandleSyncRequest(context); - } else { - // Add the thread to the list of threads that have issued a sync request with this - // server. - pending_requesting_threads.push_back(std::move(thread)); + if (hle_handler != nullptr) { + // For HLE services, we put the request threads to sleep for a short duration to + // simulate IPC overhead, but only if the HLE handler didn't put the thread to sleep for + // other reasons like an async callback. The IPC overhead is needed to prevent + // starvation when a thread only does sync requests to HLE services while a + // lower-priority thread is waiting to run. + + // This delay was approximated in a homebrew application by measuring the average time + // it takes for svcSendSyncRequest to return when performing the SetLcdForceBlack IPC + // request to the GSP:GPU service in a n3DS with firmware 11.6. The measured values have + // a high variance and vary between models. + static constexpr u64 IPCDelayNanoseconds = 39000; + thread->WakeAfterDelay(IPCDelayNanoseconds); + } else { + // Add the thread to the list of threads that have issued a sync request with this + // server. + pending_requesting_threads.push_back(std::move(thread)); + } } // If this ServerSession does not have an HLE implementation, just wake up the threads waiting @@ -140,9 +160,4 @@ ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& n return std::make_tuple(std::move(server_session), std::move(client_session)); } - -ResultCode TranslateHLERequest(ServerSession* server_session) { - // TODO(Subv): Implement this function once multiple concurrent processes are supported. - return RESULT_SUCCESS; -} } // namespace Kernel diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 1446921065..2da8070420 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -21,6 +21,7 @@ class ServerSession; class Session; class SessionRequestHandler; class Thread; +class HLERequestContext; /** * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS @@ -116,17 +117,12 @@ private: */ static ResultVal> Create(std::string name = "Unknown"); + /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an + /// object handle. + ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context); + /// When set to True, converts the session to a domain at the end of the command bool convert_to_domain{}; }; -/** - * Performs command buffer translation for an HLE IPC request. - * The command buffer from the ServerSession thread's TLS is copied into a - * buffer and all descriptors in the buffer are processed. - * TODO(Subv): Implement this function, currently we do not support multiple processes running at - * once, but once that is implemented we'll need to properly translate all descriptors - * in the command buffer. - */ -ResultCode TranslateHLERequest(ServerSession* server_session); } // namespace Kernel diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 1a33cc6cbb..130b669a08 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -284,6 +284,7 @@ void Thread::ResumeFromWait() { case THREADSTATUS_WAIT_SYNCH_ANY: case THREADSTATUS_WAIT_ARB: case THREADSTATUS_WAIT_SLEEP: + case THREADSTATUS_WAIT_IPC: break; case THREADSTATUS_READY: diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 0a1ada27dc..bbffaf4cf2 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -40,6 +40,7 @@ enum ThreadStatus { THREADSTATUS_READY, ///< Ready to run THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC + THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true THREADSTATUS_DORMANT, ///< Created but not yet made ready From cec0d4f1918c640524fa0087549da2fab0960e24 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 Feb 2018 14:46:11 -0500 Subject: [PATCH 19/28] kernel: Remove unused address_arbiter code. --- src/core/CMakeLists.txt | 2 - src/core/hle/kernel/address_arbiter.cpp | 91 ------------------------- src/core/hle/kernel/address_arbiter.h | 60 ---------------- src/core/hle/kernel/thread.cpp | 34 --------- src/core/hle/kernel/thread.h | 12 ---- 5 files changed, 199 deletions(-) delete mode 100644 src/core/hle/kernel/address_arbiter.cpp delete mode 100644 src/core/hle/kernel/address_arbiter.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ce68194c54..2f1fd7880b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -28,8 +28,6 @@ add_library(core STATIC hle/config_mem.h hle/ipc.h hle/ipc_helpers.h - hle/kernel/address_arbiter.cpp - hle/kernel/address_arbiter.h hle/kernel/client_port.cpp hle/kernel/client_port.h hle/kernel/client_session.cpp diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp deleted file mode 100644 index 776d342f08..0000000000 --- a/src/core/hle/kernel/address_arbiter.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/common_types.h" -#include "common/logging/log.h" -#include "core/hle/kernel/address_arbiter.h" -#include "core/hle/kernel/errors.h" -#include "core/hle/kernel/thread.h" -#include "core/memory.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Kernel namespace - -namespace Kernel { - -AddressArbiter::AddressArbiter() {} -AddressArbiter::~AddressArbiter() {} - -SharedPtr AddressArbiter::Create(std::string name) { - SharedPtr address_arbiter(new AddressArbiter); - - address_arbiter->name = std::move(name); - - return address_arbiter; -} - -ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, - u64 nanoseconds) { - switch (type) { - - // Signal thread(s) waiting for arbitrate address... - case ArbitrationType::Signal: - // Negative value means resume all threads - if (value < 0) { - ArbitrateAllThreads(address); - } else { - // Resume first N threads - for (int i = 0; i < value; i++) - ArbitrateHighestPriorityThread(address); - } - break; - - // Wait current thread (acquire the arbiter)... - case ArbitrationType::WaitIfLessThan: - if ((s32)Memory::Read32(address) < value) { - Kernel::WaitCurrentThread_ArbitrateAddress(address); - } - break; - case ArbitrationType::WaitIfLessThanWithTimeout: - if ((s32)Memory::Read32(address) < value) { - Kernel::WaitCurrentThread_ArbitrateAddress(address); - GetCurrentThread()->WakeAfterDelay(nanoseconds); - } - break; - case ArbitrationType::DecrementAndWaitIfLessThan: { - s32 memory_value = Memory::Read32(address); - if (memory_value < value) { - // Only change the memory value if the thread should wait - Memory::Write32(address, (s32)memory_value - 1); - Kernel::WaitCurrentThread_ArbitrateAddress(address); - } - break; - } - case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout: { - s32 memory_value = Memory::Read32(address); - if (memory_value < value) { - // Only change the memory value if the thread should wait - Memory::Write32(address, (s32)memory_value - 1); - Kernel::WaitCurrentThread_ArbitrateAddress(address); - GetCurrentThread()->WakeAfterDelay(nanoseconds); - } - break; - } - - default: - LOG_ERROR(Kernel, "unknown type=%d", type); - return ERR_INVALID_ENUM_VALUE_FND; - } - - // The calls that use a timeout seem to always return a Timeout error even if they did not put - // the thread to sleep - if (type == ArbitrationType::WaitIfLessThanWithTimeout || - type == ArbitrationType::DecrementAndWaitIfLessThanWithTimeout) { - - return RESULT_TIMEOUT; - } - return RESULT_SUCCESS; -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h deleted file mode 100644 index f902ddf2d8..0000000000 --- a/src/core/hle/kernel/address_arbiter.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "common/common_types.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/result.h" - -// Address arbiters are an underlying kernel synchronization object that can be created/used via -// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR -// applications use them as an underlying mechanism to implement thread-safe barriers, events, and -// semphores. - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Kernel namespace - -namespace Kernel { - -enum class ArbitrationType : u32 { - Signal, - WaitIfLessThan, - DecrementAndWaitIfLessThan, - WaitIfLessThanWithTimeout, - DecrementAndWaitIfLessThanWithTimeout, -}; - -class AddressArbiter final : public Object { -public: - /** - * Creates an address arbiter. - * - * @param name Optional name used for debugging. - * @returns The created AddressArbiter. - */ - static SharedPtr Create(std::string name = "Unknown"); - - std::string GetTypeName() const override { - return "Arbiter"; - } - std::string GetName() const override { - return name; - } - - static const HandleType HANDLE_TYPE = HandleType::AddressArbiter; - HandleType GetHandleType() const override { - return HANDLE_TYPE; - } - - std::string name; ///< Name of address arbiter object (optional) - - ResultCode ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, u64 nanoseconds); - -private: - AddressArbiter(); - ~AddressArbiter() override; -}; - -} // namespace Kernel diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 130b669a08..0fcc65cbf8 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -109,40 +109,6 @@ void Thread::Stop() { Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot); } -Thread* ArbitrateHighestPriorityThread(u32 address) { - Thread* highest_priority_thread = nullptr; - u32 priority = THREADPRIO_LOWEST; - - // Iterate through threads, find highest priority thread that is waiting to be arbitrated... - for (auto& thread : thread_list) { - if (!CheckWait_AddressArbiter(thread.get(), address)) - continue; - - if (thread == nullptr) - continue; - - if (thread->current_priority <= priority) { - highest_priority_thread = thread.get(); - priority = thread->current_priority; - } - } - - // If a thread was arbitrated, resume it - if (nullptr != highest_priority_thread) { - highest_priority_thread->ResumeFromWait(); - } - - return highest_priority_thread; -} - -void ArbitrateAllThreads(u32 address) { - // Resume all threads found to be waiting on the address - for (auto& thread : thread_list) { - if (CheckWait_AddressArbiter(thread.get(), address)) - thread->ResumeFromWait(); - } -} - /** * Switches the CPU's active thread context to that of the specified thread * @param new_thread The thread to switch to diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index bbffaf4cf2..aa80a51a9b 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -259,18 +259,6 @@ bool HaveReadyThreads(); */ void Reschedule(); -/** - * Arbitrate the highest priority thread that is waiting - * @param address The address for which waiting threads should be arbitrated - */ -Thread* ArbitrateHighestPriorityThread(VAddr address); - -/** - * Arbitrate all threads currently waiting. - * @param address The address for which waiting threads should be arbitrated - */ -void ArbitrateAllThreads(VAddr address); - /** * Gets the current thread */ From 2d4a6883bcfa785e990c5dbf3fab9eb73a0ed277 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 Feb 2018 14:52:09 -0500 Subject: [PATCH 20/28] core: Use shared_ptr for cpu_core. --- src/core/core.cpp | 6 +++--- src/core/core.h | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 613a98b4ca..89bb2887e3 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -141,14 +141,14 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { switch (Settings::values.cpu_core) { case Settings::CpuCore::Unicorn: - cpu_core = std::make_unique(); + cpu_core = std::make_shared(); break; case Settings::CpuCore::Dynarmic: default: #ifdef ARCHITECTURE_x86_64 - cpu_core = std::make_unique(); + cpu_core = std::make_shared(); #else - cpu_core = std::make_unique(); + cpu_core = std::make_shared(); LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); #endif break; diff --git a/src/core/core.h b/src/core/core.h index f63cc47ccb..e7599e18ce 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -140,9 +140,7 @@ private: /// AppLoader used to load the current executing application std::unique_ptr app_loader; - ///< ARM11 CPU core - std::unique_ptr cpu_core; - + std::shared_ptr cpu_core; std::unique_ptr gpu_core; /// When true, signals that a reschedule should happen From c78d495161355277d19bd9debc64161b1d4bb949 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 Feb 2018 14:58:40 -0500 Subject: [PATCH 21/28] kernel: Add Scheduler, which encapsulates the scheduling loading from Thread module. --- src/core/CMakeLists.txt | 2 + src/core/hle/kernel/scheduler.cpp | 134 ++++++++++++++++++++++++++++++ src/core/hle/kernel/scheduler.h | 74 +++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 src/core/hle/kernel/scheduler.cpp create mode 100644 src/core/hle/kernel/scheduler.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2f1fd7880b..76f70a8447 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -53,6 +53,8 @@ add_library(core STATIC hle/kernel/process.h hle/kernel/resource_limit.cpp hle/kernel/resource_limit.h + hle/kernel/scheduler.cpp + hle/kernel/scheduler.h hle/kernel/server_port.cpp hle/kernel/server_port.h hle/kernel/server_session.cpp diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp new file mode 100644 index 0000000000..2fe0d5a388 --- /dev/null +++ b/src/core/hle/kernel/scheduler.cpp @@ -0,0 +1,134 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/core_timing.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/scheduler.h" + +namespace Kernel { + +Scheduler::Scheduler(std::shared_ptr cpu_core) : cpu_core(cpu_core) {} + +Scheduler::~Scheduler() { + for (auto& thread : thread_list) { + thread->Stop(); + } +} + +bool Scheduler::HaveReadyThreads() { + return ready_queue.get_first() != nullptr; +} + +Thread* Scheduler::GetCurrentThread() const { + return current_thread.get(); +} + +Thread* Scheduler::PopNextReadyThread() { + Thread* next = nullptr; + Thread* thread = GetCurrentThread(); + + if (thread && thread->status == THREADSTATUS_RUNNING) { + // We have to do better than the current thread. + // This call returns null when that's not possible. + next = ready_queue.pop_first_better(thread->current_priority); + if (!next) { + // Otherwise just keep going with the current thread + next = thread; + } + } else { + next = ready_queue.pop_first(); + } + + return next; +} + +void Scheduler::SwitchContext(Thread* new_thread) { + Thread* previous_thread = GetCurrentThread(); + + // Save context for previous thread + if (previous_thread) { + previous_thread->last_running_ticks = CoreTiming::GetTicks(); + cpu_core->SaveContext(previous_thread->context); + + if (previous_thread->status == THREADSTATUS_RUNNING) { + // This is only the case when a reschedule is triggered without the current thread + // yielding execution (i.e. an event triggered, system core time-sliced, etc) + ready_queue.push_front(previous_thread->current_priority, previous_thread); + previous_thread->status = THREADSTATUS_READY; + } + } + + // Load context of new thread + if (new_thread) { + ASSERT_MSG(new_thread->status == THREADSTATUS_READY, + "Thread must be ready to become running."); + + // Cancel any outstanding wakeup events for this thread + new_thread->CancelWakeupTimer(); + + auto previous_process = Kernel::g_current_process; + + current_thread = new_thread; + + ready_queue.remove(new_thread->current_priority, new_thread); + new_thread->status = THREADSTATUS_RUNNING; + + if (previous_process != current_thread->owner_process) { + Kernel::g_current_process = current_thread->owner_process; + SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); + } + + cpu_core->LoadContext(new_thread->context); + cpu_core->SetTlsAddress(new_thread->GetTLSAddress()); + } else { + current_thread = nullptr; + // Note: We do not reset the current process and current page table when idling because + // technically we haven't changed processes, our threads are just paused. + } +} + +void Scheduler::Reschedule() { + Thread* cur = GetCurrentThread(); + Thread* next = PopNextReadyThread(); + + if (cur && next) { + LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId()); + } else if (cur) { + LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId()); + } else if (next) { + LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId()); + } + + SwitchContext(next); +} + +void Scheduler::AddThread(SharedPtr thread, u32 priority) { + thread_list.push_back(thread); + ready_queue.prepare(priority); +} + +void Scheduler::RemoveThread(Thread* thread) { + thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), + thread_list.end()); +} + +void Scheduler::ScheduleThread(Thread* thread, u32 priority) { + ASSERT(thread->status == THREADSTATUS_READY); + ready_queue.push_back(priority, thread); +} + +void Scheduler::UnscheduleThread(Thread* thread, u32 priority) { + ASSERT(thread->status == THREADSTATUS_READY); + ready_queue.remove(priority, thread); +} + +void Scheduler::SetThreadPriority(Thread* thread, u32 priority) { + // If thread was ready, adjust queues + if (thread->status == THREADSTATUS_READY) + ready_queue.move(thread, thread->current_priority, priority); + else + ready_queue.prepare(priority); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h new file mode 100644 index 0000000000..72e658ec66 --- /dev/null +++ b/src/core/hle/kernel/scheduler.h @@ -0,0 +1,74 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include "common/common_types.h" +#include "common/thread_queue_list.h" +#include "core/arm/arm_interface.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +class Scheduler final { +public: + Scheduler(std::shared_ptr cpu_core); + ~Scheduler(); + + /// Returns whether there are any threads that are ready to run. + bool HaveReadyThreads(); + + /// Reschedules to the next available thread (call after current thread is suspended) + void Reschedule(); + + /// Gets the current running thread + Thread* GetCurrentThread() const; + + /// Adds a new thread to the scheduler + void AddThread(SharedPtr thread, u32 priority); + + /// Removes a thread from the scheduler + void RemoveThread(Thread* thread); + + /// Schedules a thread that has become "ready" + void ScheduleThread(Thread* thread, u32 priority); + + /// Unschedules a thread that was already scheduled + void UnscheduleThread(Thread* thread, u32 priority); + + /// Sets the priority of a thread in the scheduler + void SetThreadPriority(Thread* thread, u32 priority); + + /// Returns a list of all threads managed by the scheduler + const std::vector>& GetThreadList() const { + return thread_list; + } + +private: + /** + * Pops and returns the next thread from the thread queue + * @return A pointer to the next ready thread + */ + Thread* PopNextReadyThread(); + + /** + * Switches the CPU's active thread context to that of the specified thread + * @param new_thread The thread to switch to + */ + void SwitchContext(Thread* new_thread); + + /// Lists all thread ids that aren't deleted/etc. + std::vector> thread_list; + + /// Lists only ready thread ids. + Common::ThreadQueueList ready_queue; + + SharedPtr current_thread = nullptr; + + std::shared_ptr cpu_core; +}; + +} // namespace Kernel From ac81c02ed9401b137e83bece2edd6dee8d0a0af2 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 Feb 2018 15:17:16 -0500 Subject: [PATCH 22/28] kernel: Use Scheduler class for threading. --- src/core/core.cpp | 3 +- src/core/core.h | 6 ++ src/core/hle/kernel/svc.cpp | 4 +- src/core/hle/kernel/thread.cpp | 169 +++----------------------------- src/core/hle/kernel/thread.h | 15 --- src/yuzu/debugger/wait_tree.cpp | 3 +- 6 files changed, 26 insertions(+), 174 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 89bb2887e3..cef697dfe9 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -133,7 +133,7 @@ void System::Reschedule() { } reschedule_pending = false; - Kernel::Reschedule(); + Core::System::GetInstance().Scheduler().Reschedule(); } System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { @@ -154,6 +154,7 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { break; } + scheduler = std::make_unique(cpu_core); gpu_core = std::make_unique(); telemetry_session = std::make_unique(); diff --git a/src/core/core.h b/src/core/core.h index e7599e18ce..ada23b3478 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -7,6 +7,7 @@ #include #include #include "common/common_types.h" +#include "core/hle/kernel/scheduler.h" #include "core/loader/loader.h" #include "core/memory.h" #include "core/perf_stats.h" @@ -107,6 +108,10 @@ public: return *gpu_core; } + Kernel::Scheduler& Scheduler() { + return *scheduler; + } + PerfStats perf_stats; FrameLimiter frame_limiter; @@ -141,6 +146,7 @@ private: std::unique_ptr app_loader; std::shared_ptr cpu_core; + std::unique_ptr scheduler; std::unique_ptr gpu_core; /// When true, signals that a reschedule should happen diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 4d20ef1342..cbd5b69aa1 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -483,7 +483,7 @@ static void ExitProcess() { g_current_process->status = ProcessStatus::Exited; // Stop all the process threads that are currently waiting for objects. - auto& thread_list = GetThreadList(); + auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList(); for (auto& thread : thread_list) { if (thread->owner_process != g_current_process) continue; @@ -585,7 +585,7 @@ static void SleepThread(s64 nanoseconds) { // Don't attempt to yield execution if there are no available threads to run, // this way we avoid a useless reschedule to the idle thread. - if (nanoseconds == 0 && !HaveReadyThreads()) + if (nanoseconds == 0 && !Core::System::GetInstance().Scheduler().HaveReadyThreads()) return; // Sleep current thread and check for next thread to schedule diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 0fcc65cbf8..dd0a8ae486 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -41,14 +41,6 @@ void Thread::Acquire(Thread* thread) { // us to simply use a pool index or similar. static Kernel::HandleTable wakeup_callback_handle_table; -// Lists all thread ids that aren't deleted/etc. -static std::vector> thread_list; - -// Lists only ready thread ids. -static Common::ThreadQueueList ready_queue; - -static SharedPtr current_thread; - // The first available thread id at startup static u32 next_thread_id; @@ -63,10 +55,6 @@ inline static u32 const NewThreadId() { Thread::Thread() {} Thread::~Thread() {} -Thread* GetCurrentThread() { - return current_thread.get(); -} - /** * Check if the specified thread is waiting on the specified address to be arbitrated * @param thread The thread to test @@ -86,7 +74,7 @@ void Thread::Stop() { // Clean up thread from ready queue // This is only needed when the thread is termintated forcefully (SVC TerminateProcess) if (status == THREADSTATUS_READY) { - ready_queue.remove(current_priority, this); + Core::System::GetInstance().Scheduler().UnscheduleThread(this, current_priority); } status = THREADSTATUS_DEAD; @@ -109,78 +97,6 @@ void Thread::Stop() { Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot); } -/** - * Switches the CPU's active thread context to that of the specified thread - * @param new_thread The thread to switch to - */ -static void SwitchContext(Thread* new_thread) { - Thread* previous_thread = GetCurrentThread(); - - // Save context for previous thread - if (previous_thread) { - previous_thread->last_running_ticks = CoreTiming::GetTicks(); - Core::CPU().SaveContext(previous_thread->context); - - if (previous_thread->status == THREADSTATUS_RUNNING) { - // This is only the case when a reschedule is triggered without the current thread - // yielding execution (i.e. an event triggered, system core time-sliced, etc) - ready_queue.push_front(previous_thread->current_priority, previous_thread); - previous_thread->status = THREADSTATUS_READY; - } - } - - // Load context of new thread - if (new_thread) { - ASSERT_MSG(new_thread->status == THREADSTATUS_READY, - "Thread must be ready to become running."); - - // Cancel any outstanding wakeup events for this thread - CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); - - auto previous_process = Kernel::g_current_process; - - current_thread = new_thread; - - ready_queue.remove(new_thread->current_priority, new_thread); - new_thread->status = THREADSTATUS_RUNNING; - - if (previous_process != current_thread->owner_process) { - Kernel::g_current_process = current_thread->owner_process; - SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); - } - - Core::CPU().LoadContext(new_thread->context); - Core::CPU().SetTlsAddress(new_thread->GetTLSAddress()); - } else { - current_thread = nullptr; - // Note: We do not reset the current process and current page table when idling because - // technically we haven't changed processes, our threads are just paused. - } -} - -/** - * Pops and returns the next thread from the thread queue - * @return A pointer to the next ready thread - */ -static Thread* PopNextReadyThread() { - Thread* next; - Thread* thread = GetCurrentThread(); - - if (thread && thread->status == THREADSTATUS_RUNNING) { - // We have to do better than the current thread. - // This call returns null when that's not possible. - next = ready_queue.pop_first_better(thread->current_priority); - if (!next) { - // Otherwise just keep going with the current thread - next = thread; - } - } else { - next = ready_queue.pop_first(); - } - - return next; -} - void WaitCurrentThread_Sleep() { Thread* thread = GetCurrentThread(); thread->status = THREADSTATUS_WAIT_SLEEP; @@ -195,8 +111,7 @@ void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { void ExitCurrentThread() { Thread* thread = GetCurrentThread(); thread->Stop(); - thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), - thread_list.end()); + Core::System::GetInstance().Scheduler().RemoveThread(thread); } /** @@ -274,31 +189,11 @@ void Thread::ResumeFromWait() { wakeup_callback = nullptr; - ready_queue.push_back(current_priority, this); status = THREADSTATUS_READY; + Core::System::GetInstance().Scheduler().ScheduleThread(this, current_priority); Core::System::GetInstance().PrepareReschedule(); } -/** - * Prints the thread queue for debugging purposes - */ -static void DebugThreadQueue() { - Thread* thread = GetCurrentThread(); - if (!thread) { - LOG_DEBUG(Kernel, "Current: NO CURRENT THREAD"); - } else { - LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority, - GetCurrentThread()->GetObjectId()); - } - - for (auto& t : thread_list) { - u32 priority = ready_queue.contains(t.get()); - if (priority != -1) { - LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); - } - } -} - /** * Finds a free location for the TLS section of a thread. * @param tls_slots The TLS page array of the thread's owner process. @@ -366,8 +261,7 @@ ResultVal> Thread::Create(std::string name, VAddr entry_point, SharedPtr thread(new Thread); - thread_list.push_back(thread); - ready_queue.prepare(priority); + Core::System::GetInstance().Scheduler().AddThread(thread, priority); thread->thread_id = NewThreadId(); thread->status = THREADSTATUS_DORMANT; @@ -438,12 +332,7 @@ ResultVal> Thread::Create(std::string name, VAddr entry_point, void Thread::SetPriority(u32 priority) { ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, "Invalid priority value."); - // If thread was ready, adjust queues - if (status == THREADSTATUS_READY) - ready_queue.move(this, current_priority, priority); - else - ready_queue.prepare(priority); - + Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority); nominal_priority = current_priority = priority; } @@ -457,11 +346,7 @@ void Thread::UpdatePriority() { } void Thread::BoostPriority(u32 priority) { - // If thread was ready, adjust queues - if (status == THREADSTATUS_READY) - ready_queue.move(this, current_priority, priority); - else - ready_queue.prepare(priority); + Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority); current_priority = priority; } @@ -487,25 +372,6 @@ SharedPtr SetupMainThread(VAddr entry_point, u32 priority, return thread; } -bool HaveReadyThreads() { - return ready_queue.get_first() != nullptr; -} - -void Reschedule() { - Thread* cur = GetCurrentThread(); - Thread* next = PopNextReadyThread(); - - if (cur && next) { - LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId()); - } else if (cur) { - LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId()); - } else if (next) { - LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId()); - } - - SwitchContext(next); -} - void Thread::SetWaitSynchronizationResult(ResultCode result) { context.cpu_registers[0] = result.raw; } @@ -528,25 +394,18 @@ VAddr Thread::GetCommandBufferAddress() const { //////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Gets the current thread + */ +Thread* GetCurrentThread() { + return Core::System::GetInstance().Scheduler().GetCurrentThread(); +} + void ThreadingInit() { ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); - - current_thread = nullptr; next_thread_id = 1; } -void ThreadingShutdown() { - current_thread = nullptr; - - for (auto& t : thread_list) { - t->Stop(); - } - thread_list.clear(); - ready_queue.clear(); -} - -const std::vector>& GetThreadList() { - return thread_list; -} +void ThreadingShutdown() {} } // namespace Kernel diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index aa80a51a9b..4fd2fc2f85 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -249,16 +249,6 @@ private: SharedPtr SetupMainThread(VAddr entry_point, u32 priority, SharedPtr owner_process); -/** - * Returns whether there are any threads that are ready to run. - */ -bool HaveReadyThreads(); - -/** - * Reschedules to the next available thread (call after current thread is suspended) - */ -void Reschedule(); - /** * Gets the current thread */ @@ -290,9 +280,4 @@ void ThreadingInit(); */ void ThreadingShutdown(); -/** - * Get a const reference to the thread list for debug use - */ -const std::vector>& GetThreadList(); - } // namespace Kernel diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index e4a6d16ae1..7a62f57b54 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -5,6 +5,7 @@ #include "yuzu/debugger/wait_tree.h" #include "yuzu/util/util.h" +#include "core/core.h" #include "core/hle/kernel/condition_variable.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/mutex.h" @@ -50,7 +51,7 @@ std::size_t WaitTreeItem::Row() const { } std::vector> WaitTreeItem::MakeThreadItemList() { - const auto& threads = Kernel::GetThreadList(); + const auto& threads = Core::System::GetInstance().Scheduler().GetThreadList(); std::vector> item_list; item_list.reserve(threads.size()); for (std::size_t i = 0; i < threads.size(); ++i) { From 5ab285f1f944d8979530e1777fcf584377ac4d2d Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 18 Feb 2018 18:09:52 -0500 Subject: [PATCH 23/28] AM: Corrected the response in EnsureSaveData. The values are still unknown and the function is still considered a stub. Puyo Puyo Tetris now tries to call fsp-srv:MountSaveData. --- src/core/hle/service/am/am.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 402105ea07..03305814fe 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -378,8 +378,9 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2}; + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); + rb.Push(0); } void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { From 8db80d8389689af5f1b8bf7f8a01f98b9cea4c09 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 19 Feb 2018 16:46:42 -0500 Subject: [PATCH 24/28] scheduler: Cleanup based on PR feedback. --- src/core/core.cpp | 2 +- src/core/hle/kernel/scheduler.cpp | 2 +- src/core/hle/kernel/scheduler.h | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index cef697dfe9..8c5dd3761a 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -154,7 +154,7 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { break; } - scheduler = std::make_unique(cpu_core); + scheduler = std::make_unique(cpu_core.get()); gpu_core = std::make_unique(); telemetry_session = std::make_unique(); diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 2fe0d5a388..235068b22f 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -8,7 +8,7 @@ namespace Kernel { -Scheduler::Scheduler(std::shared_ptr cpu_core) : cpu_core(cpu_core) {} +Scheduler::Scheduler(ARM_Interface* cpu_core) : cpu_core(cpu_core) {} Scheduler::~Scheduler() { for (auto& thread : thread_list) { diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 72e658ec66..27d0247d65 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -4,7 +4,6 @@ #pragma once -#include #include #include "common/common_types.h" #include "common/thread_queue_list.h" @@ -15,7 +14,7 @@ namespace Kernel { class Scheduler final { public: - Scheduler(std::shared_ptr cpu_core); + explicit Scheduler(ARM_Interface* cpu_core); ~Scheduler(); /// Returns whether there are any threads that are ready to run. @@ -68,7 +67,7 @@ private: SharedPtr current_thread = nullptr; - std::shared_ptr cpu_core; + ARM_Interface* cpu_core; }; } // namespace Kernel From 1d491d636d5d4f4223ce3389635b35b938c9c498 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 19 Feb 2018 17:31:54 -0500 Subject: [PATCH 25/28] logging: Add category for Friend service. --- src/common/logging/backend.cpp | 1 + src/common/logging/log.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 8274b23886..dc989cd731 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -36,6 +36,7 @@ namespace Log { SUB(Service, Audio) \ SUB(Service, AM) \ SUB(Service, APM) \ + SUB(Service, Friend) \ SUB(Service, FS) \ SUB(Service, HID) \ SUB(Service, LM) \ diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 69472ef1ac..18a87f1c70 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -53,6 +53,7 @@ enum class Class : ClassType { Service_AM, ///< The AM (Applet manager) service Service_APM, ///< The APM (Performance) service Service_Audio, ///< The Audio (Audio control) service + Service_Friend, ///< The friend service Service_FS, ///< The FS (Filesystem) service Service_HID, ///< The HID (Human interface device) service Service_LM, ///< The LM (Logger) service From 7bee3427d0845a9d1fb3b84f4327e2a05491a058 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 19 Feb 2018 17:34:02 -0500 Subject: [PATCH 26/28] service: Add Friend service interface. --- src/core/CMakeLists.txt | 4 ++++ src/core/hle/service/friend/friend.cpp | 28 +++++++++++++++++++++++ src/core/hle/service/friend/friend.h | 29 ++++++++++++++++++++++++ src/core/hle/service/friend/friend_a.cpp | 19 ++++++++++++++++ src/core/hle/service/friend/friend_a.h | 18 +++++++++++++++ src/core/hle/service/service.cpp | 2 ++ 6 files changed, 100 insertions(+) create mode 100644 src/core/hle/service/friend/friend.cpp create mode 100644 src/core/hle/service/friend/friend.h create mode 100644 src/core/hle/service/friend/friend_a.cpp create mode 100644 src/core/hle/service/friend/friend_a.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 76f70a8447..ec011787eb 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -112,6 +112,10 @@ add_library(core STATIC hle/service/filesystem/filesystem.h hle/service/filesystem/fsp_srv.cpp hle/service/filesystem/fsp_srv.h + hle/service/friend/friend.cpp + hle/service/friend/friend.h + hle/service/friend/friend_a.cpp + hle/service/friend/friend_a.h hle/service/hid/hid.cpp hle/service/hid/hid.h hle/service/lm/lm.cpp diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp new file mode 100644 index 0000000000..26593bb0cd --- /dev/null +++ b/src/core/hle/service/friend/friend.cpp @@ -0,0 +1,28 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/friend/friend.h" +#include "core/hle/service/friend/friend_a.h" + +namespace Service { +namespace Friend { + +void Module::Interface::Unknown(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_Friend, "(STUBBED) called"); +} + +Module::Interface::Interface(std::shared_ptr module, const char* name) + : ServiceFramework(name), module(std::move(module)) {} + +void InstallInterfaces(SM::ServiceManager& service_manager) { + auto module = std::make_shared(); + std::make_shared(module)->InstallAsService(service_manager); +} + +} // namespace Friend +} // namespace Service diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h new file mode 100644 index 0000000000..ffa498397e --- /dev/null +++ b/src/core/hle/service/friend/friend.h @@ -0,0 +1,29 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Friend { + +class Module final { +public: + class Interface : public ServiceFramework { + public: + Interface(std::shared_ptr module, const char* name); + + void Unknown(Kernel::HLERequestContext& ctx); + + protected: + std::shared_ptr module; + }; +}; + +/// Registers all Friend services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace Friend +} // namespace Service diff --git a/src/core/hle/service/friend/friend_a.cpp b/src/core/hle/service/friend/friend_a.cpp new file mode 100644 index 0000000000..e1f2397c27 --- /dev/null +++ b/src/core/hle/service/friend/friend_a.cpp @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/friend/friend_a.h" + +namespace Service { +namespace Friend { + +Friend_A::Friend_A(std::shared_ptr module) + : Module::Interface(std::move(module), "friend:a") { + static const FunctionInfo functions[] = { + {0, &Friend_A::Unknown, "Unknown"}, + }; + RegisterHandlers(functions); +} + +} // namespace Friend +} // namespace Service diff --git a/src/core/hle/service/friend/friend_a.h b/src/core/hle/service/friend/friend_a.h new file mode 100644 index 0000000000..68fa58297c --- /dev/null +++ b/src/core/hle/service/friend/friend_a.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/friend/friend.h" + +namespace Service { +namespace Friend { + +class Friend_A final : public Module::Interface { +public: + explicit Friend_A(std::shared_ptr module); +}; + +} // namespace Friend +} // namespace Service diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 5e6d837290..6a2d6a4ef7 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -20,6 +20,7 @@ #include "core/hle/service/apm/apm.h" #include "core/hle/service/audio/audio.h" #include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/friend/friend.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/lm/lm.h" #include "core/hle/service/nifm/nifm.h" @@ -180,6 +181,7 @@ void Init() { APM::InstallInterfaces(*SM::g_service_manager); Audio::InstallInterfaces(*SM::g_service_manager); FileSystem::InstallInterfaces(*SM::g_service_manager); + Friend::InstallInterfaces(*SM::g_service_manager); HID::InstallInterfaces(*SM::g_service_manager); LM::InstallInterfaces(*SM::g_service_manager); NIFM::InstallInterfaces(*SM::g_service_manager); From 678574972ad9ad9e018810663aee122bd6b00234 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 19 Feb 2018 17:39:41 -0500 Subject: [PATCH 27/28] acc_u0: Stub ListOpenUsers service function. --- src/core/hle/service/acc/acc_u0.cpp | 11 ++++++++++- src/core/hle/service/acc/acc_u0.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index 7955f726be..52c3491d5f 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -65,11 +65,19 @@ void ACC_U0::GetUserExistence(Kernel::HLERequestContext& ctx) { } void ACC_U0::ListAllUsers(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + constexpr std::array user_ids{DEFAULT_USER_ID}; + ctx.WriteBuffer(user_ids.data(), user_ids.size()); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void ACC_U0::ListOpenUsers(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); constexpr std::array user_ids{DEFAULT_USER_ID}; ctx.WriteBuffer(user_ids.data(), user_ids.size()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_ACC, "called"); } void ACC_U0::GetProfile(Kernel::HLERequestContext& ctx) { @@ -103,6 +111,7 @@ ACC_U0::ACC_U0() : ServiceFramework("acc:u0") { static const FunctionInfo functions[] = { {1, &ACC_U0::GetUserExistence, "GetUserExistence"}, {2, &ACC_U0::ListAllUsers, "ListAllUsers"}, + {3, &ACC_U0::ListOpenUsers, "ListOpenUsers"}, {4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"}, {5, &ACC_U0::GetProfile, "GetProfile"}, {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, diff --git a/src/core/hle/service/acc/acc_u0.h b/src/core/hle/service/acc/acc_u0.h index d7732e75b7..222f372820 100644 --- a/src/core/hle/service/acc/acc_u0.h +++ b/src/core/hle/service/acc/acc_u0.h @@ -29,6 +29,7 @@ public: private: void GetUserExistence(Kernel::HLERequestContext& ctx); void ListAllUsers(Kernel::HLERequestContext& ctx); + void ListOpenUsers(Kernel::HLERequestContext& ctx); void GetLastOpenedUser(Kernel::HLERequestContext& ctx); void GetProfile(Kernel::HLERequestContext& ctx); void InitializeApplicationInfo(Kernel::HLERequestContext& ctx); From 46931a95660e73acf971a26bb7f341af6e40d4c8 Mon Sep 17 00:00:00 2001 From: mailwl Date: Tue, 20 Feb 2018 10:27:32 +0300 Subject: [PATCH 28/28] Service/AOC: stub ListAddOnContent function --- src/common/logging/backend.cpp | 1 + src/common/logging/log.h | 1 + src/core/hle/service/aoc/aoc_u.cpp | 25 +++++++++++++++++++++++-- src/core/hle/service/aoc/aoc_u.h | 3 +++ 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 8274b23886..3a9a15a36d 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -35,6 +35,7 @@ namespace Log { SUB(Service, ACC) \ SUB(Service, Audio) \ SUB(Service, AM) \ + SUB(Service, AOC) \ SUB(Service, APM) \ SUB(Service, FS) \ SUB(Service, HID) \ diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 69472ef1ac..498569c5c9 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -51,6 +51,7 @@ enum class Class : ClassType { /// should have its own subclass. Service_ACC, ///< The ACC (Accounts) service Service_AM, ///< The AM (Applet manager) service + Service_AOC, ///< The AOC (AddOn Content) service Service_APM, ///< The APM (Performance) service Service_Audio, ///< The Audio (Audio control) service Service_FS, ///< The FS (Filesystem) service diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 2606832013..430b2a7ada 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -2,16 +2,37 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/service/aoc/aoc_u.h" namespace Service { namespace AOC { +AOC_U::AOC_U() : ServiceFramework("aoc:u") { + static const FunctionInfo functions[] = { + {0, nullptr, "CountAddOnContentByApplicationId"}, + {1, nullptr, "ListAddOnContentByApplicationId"}, + {2, nullptr, "CountAddOnContent"}, + {3, &AOC_U::ListAddOnContent, "ListAddOnContent"}, + {4, nullptr, "GetAddOnContentBaseIdByApplicationId"}, + {5, nullptr, "GetAddOnContentBaseId"}, + {6, nullptr, "PrepareAddOnContentByApplicationId"}, + {7, nullptr, "PrepareAddOnContent"}, + }; + RegisterHandlers(functions); +} + +void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(0); + LOG_WARNING(Service_AOC, "(STUBBED) called"); +} + void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared()->InstallAsService(service_manager); } -AOC_U::AOC_U() : ServiceFramework("aoc:u") {} - } // namespace AOC } // namespace Service diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h index 0cbbf1e5de..4d6720a9d7 100644 --- a/src/core/hle/service/aoc/aoc_u.h +++ b/src/core/hle/service/aoc/aoc_u.h @@ -13,6 +13,9 @@ class AOC_U final : public ServiceFramework { public: AOC_U(); ~AOC_U() = default; + +private: + void ListAddOnContent(Kernel::HLERequestContext& ctx); }; /// Registers all AOC services with the specified service manager.