From 8dee5663b3b2ae5dd1b5a4b9eeacf030caa0de9a Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 13 Feb 2018 22:12:46 -0500 Subject: [PATCH 1/9] 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 e44644624..6a55ff96d 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -17,6 +17,13 @@ namespace Devices { class nvdevice; } +struct IoctlFence { + u32 id; + u32 value; +}; + +static_assert(sizeof(IoctlFence) == 8, "IoctlFence has wrong size"); + class Module final { public: Module(); diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index ff5005f71..9394a06a7 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 2/9] 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 9394a06a7..439af318c 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 3/9] 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 439af318c..7a71a4b3d 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 4/9] 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 7a71a4b3d..048fe3a6f 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 5/9] 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 ee99ab280..45711d686 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 fd02a5e45..0ca01aa6d 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 1b64160d835d23141cb150cb1ea9b2ab62b5c835 Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 17 Feb 2018 13:59:45 -0500 Subject: [PATCH 6/9] 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 048fe3a6f..e5f196158 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 7/9] 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 e5f196158..1afd5a4fb 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 8/9] 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 1afd5a4fb..0aa621dfe 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 9/9] 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 cd8c0c605..b3842eb4c 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;