Ver Fonte

Merge pull request #4907 from ogniK5377/nvdrv-cleanup

core: Make nvservices more standardized
bunnei há 5 anos atrás
pai
commit
afd0e2ee87
26 ficheiros alterados com 1220 adições e 898 exclusões
  1. 24 12
      src/core/hle/service/nvdrv/devices/nvdevice.h
  2. 16 5
      src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
  3. 5 3
      src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
  4. 99 58
      src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
  5. 52 58
      src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
  6. 43 32
      src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
  7. 45 64
      src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
  8. 133 59
      src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
  9. 20 35
      src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
  10. 105 79
      src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
  11. 65 88
      src/core/hle/service/nvdrv/devices/nvhost_gpu.h
  12. 50 38
      src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
  13. 5 20
      src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
  14. 19 24
      src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
  15. 69 41
      src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
  16. 29 13
      src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
  17. 8 10
      src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
  18. 42 28
      src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
  19. 5 19
      src/core/hle/service/nvdrv/devices/nvhost_vic.h
  20. 65 54
      src/core/hle/service/nvdrv/devices/nvmap.cpp
  21. 31 37
      src/core/hle/service/nvdrv/devices/nvmap.h
  22. 123 76
      src/core/hle/service/nvdrv/interface.cpp
  23. 4 2
      src/core/hle/service/nvdrv/interface.h
  24. 66 20
      src/core/hle/service/nvdrv/nvdata.h
  25. 81 16
      src/core/hle/service/nvdrv/nvdrv.cpp
  26. 16 7
      src/core/hle/service/nvdrv/nvdrv.h

+ 24 - 12
src/core/hle/service/nvdrv/devices/nvdevice.h

@@ -24,25 +24,37 @@ public:
     explicit nvdevice(Core::System& system) : system{system} {}
     virtual ~nvdevice() = default;
 
-    union Ioctl {
-        u32_le raw;
-        BitField<0, 8, u32> cmd;
-        BitField<8, 8, u32> group;
-        BitField<16, 14, u32> length;
-        BitField<30, 1, u32> is_in;
-        BitField<31, 1, u32> is_out;
-    };
+    /**
+     * Handles an ioctl1 request.
+     * @param command The ioctl command id.
+     * @param input A buffer containing the input data for the ioctl.
+     * @param output A buffer where the output data will be written to.
+     * @returns The result code of the ioctl.
+     */
+    virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input,
+                            std::vector<u8>& output) = 0;
+
+    /**
+     * Handles an ioctl2 request.
+     * @param command The ioctl command id.
+     * @param input A buffer containing the input data for the ioctl.
+     * @param inline_input A buffer containing the input data for the ioctl which has been inlined.
+     * @param output A buffer where the output data will be written to.
+     * @returns The result code of the ioctl.
+     */
+    virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+                            const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
 
     /**
-     * Handles an ioctl request.
+     * Handles an ioctl3 request.
      * @param command The ioctl command id.
      * @param input A buffer containing the input data for the ioctl.
      * @param output A buffer where the output data will be written to.
+     * @param inline_output A buffer where the inlined output data will be written to.
      * @returns The result code of the ioctl.
      */
-    virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-                      std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-                      IoctlVersion version) = 0;
+    virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                            std::vector<u8>& inline_output) = 0;
 
 protected:
     Core::System& system;

+ 16 - 5
src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp

@@ -18,11 +18,22 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_de
     : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
 nvdisp_disp0 ::~nvdisp_disp0() = default;
 
-u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-                        std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-                        IoctlVersion version) {
-    UNIMPLEMENTED_MSG("Unimplemented ioctl");
-    return 0;
+NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input,
+                              std::vector<u8>& output) {
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
+
+NvResult nvdisp_disp0::Ioctl2(Ioctl command, const std::vector<u8>& input,
+                              const std::vector<u8>& inline_input, std::vector<u8>& output) {
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
+
+NvResult nvdisp_disp0::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                              std::vector<u8>& inline_output) {
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
 }
 
 void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,

+ 5 - 3
src/core/hle/service/nvdrv/devices/nvdisp_disp0.h

@@ -20,9 +20,11 @@ public:
     explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
     ~nvdisp_disp0() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-              std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-              IoctlVersion version) override;
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+    NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                    std::vector<u8>& inline_output) override;
 
     /// Performs a screen flip, drawing the buffer pointed to by the handle.
     void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,

+ 99 - 58
src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp

@@ -17,59 +17,77 @@
 
 namespace Service::Nvidia::Devices {
 
-namespace NvErrCodes {
-constexpr u32 Success{};
-constexpr u32 OutOfMemory{static_cast<u32>(-12)};
-constexpr u32 InvalidInput{static_cast<u32>(-22)};
-} // namespace NvErrCodes
-
 nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
     : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
 nvhost_as_gpu::~nvhost_as_gpu() = default;
 
-u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-                         std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-                         IoctlVersion version) {
-    LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
-              command.raw, input.size(), output.size());
-
-    switch (static_cast<IoctlCommand>(command.raw)) {
-    case IoctlCommand::IocInitalizeExCommand:
-        return InitalizeEx(input, output);
-    case IoctlCommand::IocAllocateSpaceCommand:
-        return AllocateSpace(input, output);
-    case IoctlCommand::IocMapBufferExCommand:
-        return MapBufferEx(input, output);
-    case IoctlCommand::IocBindChannelCommand:
-        return BindChannel(input, output);
-    case IoctlCommand::IocGetVaRegionsCommand:
-        return GetVARegions(input, output);
-    case IoctlCommand::IocUnmapBufferCommand:
-        return UnmapBuffer(input, output);
-    case IoctlCommand::IocFreeSpaceCommand:
-        return FreeSpace(input, output);
+NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
+                               std::vector<u8>& output) {
+    switch (command.group) {
+    case 'A':
+        switch (command.cmd) {
+        case 0x1:
+            return BindChannel(input, output);
+        case 0x2:
+            return AllocateSpace(input, output);
+        case 0x3:
+            return FreeSpace(input, output);
+        case 0x5:
+            return UnmapBuffer(input, output);
+        case 0x6:
+            return MapBufferEx(input, output);
+        case 0x8:
+            return GetVARegions(input, output);
+        case 0x9:
+            return InitalizeEx(input, output);
+        case 0x14:
+            return Remap(input, output);
+        default:
+            break;
+        }
+        break;
     default:
         break;
     }
 
-    if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) {
-        return Remap(input, output);
-    }
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
+
+NvResult nvhost_as_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
+                               const std::vector<u8>& inline_input, std::vector<u8>& output) {
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
 
-    UNIMPLEMENTED_MSG("Unimplemented ioctl command");
-    return 0;
+NvResult nvhost_as_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                               std::vector<u8>& inline_output) {
+    switch (command.group) {
+    case 'A':
+        switch (command.cmd) {
+        case 0x8:
+            return GetVARegions(input, output, inline_output);
+        default:
+            break;
+        }
+        break;
+    default:
+        break;
+    }
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
 }
 
-u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlInitalizeEx params{};
     std::memcpy(&params, input.data(), input.size());
 
     LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size);
 
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlAllocSpace params{};
     std::memcpy(&params, input.data(), input.size());
 
@@ -83,17 +101,17 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>&
         params.offset = system.GPU().MemoryManager().Allocate(size, params.align);
     }
 
-    auto result{NvErrCodes::Success};
+    auto result = NvResult::Success;
     if (!params.offset) {
         LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size);
-        result = NvErrCodes::OutOfMemory;
+        result = NvResult::InsufficientMemory;
     }
 
     std::memcpy(output.data(), &params, output.size());
     return result;
 }
 
-u32 nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlFreeSpace params{};
     std::memcpy(&params, input.data(), input.size());
 
@@ -104,15 +122,15 @@ u32 nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& outp
                                        static_cast<std::size_t>(params.pages) * params.page_size);
 
     std::memcpy(output.data(), &params, output.size());
-    return NvErrCodes::Success;
+    return NvResult::Success;
 }
 
-u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
     const auto num_entries = input.size() / sizeof(IoctlRemapEntry);
 
     LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
 
-    auto result{NvErrCodes::Success};
+    auto result = NvResult::Success;
     std::vector<IoctlRemapEntry> entries(num_entries);
     std::memcpy(entries.data(), input.data(), input.size());
 
@@ -123,7 +141,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
         const auto object{nvmap_dev->GetObject(entry.nvmap_handle)};
         if (!object) {
             LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle);
-            result = NvErrCodes::InvalidInput;
+            result = NvResult::InvalidState;
             break;
         }
 
@@ -134,7 +152,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
 
         if (!addr) {
             LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!");
-            result = NvErrCodes::InvalidInput;
+            result = NvResult::InvalidState;
             break;
         }
     }
@@ -143,7 +161,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
     return result;
 }
 
-u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlMapBufferEx params{};
     std::memcpy(&params, input.data(), input.size());
 
@@ -157,7 +175,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
     if (!object) {
         LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle);
         std::memcpy(output.data(), &params, output.size());
-        return NvErrCodes::InvalidInput;
+        return NvResult::InvalidState;
     }
 
     // The real nvservices doesn't make a distinction between handles and ids, and
@@ -184,16 +202,16 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
                              params.mapping_size, params.offset);
 
                 std::memcpy(output.data(), &params, output.size());
-                return NvErrCodes::InvalidInput;
+                return NvResult::InvalidState;
             }
 
             std::memcpy(output.data(), &params, output.size());
-            return NvErrCodes::Success;
+            return NvResult::Success;
         } else {
             LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset);
 
             std::memcpy(output.data(), &params, output.size());
-            return NvErrCodes::InvalidInput;
+            return NvResult::InvalidState;
         }
     }
 
@@ -213,10 +231,10 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
         params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size);
     }
 
-    auto result{NvErrCodes::Success};
+    auto result = NvResult::Success;
     if (!params.offset) {
         LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size);
-        result = NvErrCodes::InvalidInput;
+        result = NvResult::InvalidState;
     } else {
         AddBufferMap(params.offset, size, physical_address, is_alloc);
     }
@@ -225,7 +243,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
     return result;
 }
 
-u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlUnmapBuffer params{};
     std::memcpy(&params, input.data(), input.size());
 
@@ -238,20 +256,42 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
     }
 
     std::memcpy(output.data(), &params, output.size());
-    return NvErrCodes::Success;
+    return NvResult::Success;
 }
 
-u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlBindChannel params{};
     std::memcpy(&params, input.data(), input.size());
-
-    LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
+    LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}", params.fd);
 
     channel = params.fd;
-    return 0;
+    return NvResult::Success;
+}
+
+NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
+    IoctlGetVaRegions params{};
+    std::memcpy(&params, input.data(), input.size());
+
+    LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
+                params.buf_size);
+
+    params.buf_size = 0x30;
+    params.regions[0].offset = 0x04000000;
+    params.regions[0].page_size = 0x1000;
+    params.regions[0].pages = 0x3fbfff;
+
+    params.regions[1].offset = 0x04000000;
+    params.regions[1].page_size = 0x10000;
+    params.regions[1].pages = 0x1bffff;
+
+    // TODO(ogniK): This probably can stay stubbed but should add support way way later
+
+    std::memcpy(output.data(), &params, output.size());
+    return NvResult::Success;
 }
 
-u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output,
+                                     std::vector<u8>& inline_output) {
     IoctlGetVaRegions params{};
     std::memcpy(&params, input.data(), input.size());
 
@@ -270,7 +310,8 @@ u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& o
     // TODO(ogniK): This probably can stay stubbed but should add support way way later
 
     std::memcpy(output.data(), &params, output.size());
-    return 0;
+    std::memcpy(inline_output.data(), &params.regions, inline_output.size());
+    return NvResult::Success;
 }
 
 std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const {

+ 52 - 58
src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h

@@ -30,9 +30,11 @@ public:
     explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
     ~nvhost_as_gpu() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-              std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-              IoctlVersion version) override;
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+    NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                    std::vector<u8>& inline_output) override;
 
 private:
     class BufferMap final {
@@ -74,32 +76,21 @@ private:
         bool is_allocated{};
     };
 
-    enum class IoctlCommand : u32_le {
-        IocInitalizeExCommand = 0x40284109,
-        IocAllocateSpaceCommand = 0xC0184102,
-        IocRemapCommand = 0x00000014,
-        IocMapBufferExCommand = 0xC0284106,
-        IocBindChannelCommand = 0x40044101,
-        IocGetVaRegionsCommand = 0xC0404108,
-        IocUnmapBufferCommand = 0xC0084105,
-        IocFreeSpaceCommand = 0xC0104103,
-    };
-
     struct IoctlInitalizeEx {
-        u32_le big_page_size; // depends on GPU's available_big_page_sizes; 0=default
-        s32_le as_fd;         // ignored; passes 0
-        u32_le flags;         // passes 0
-        u32_le reserved;      // ignored; passes 0
-        u64_le unk0;
-        u64_le unk1;
-        u64_le unk2;
+        u32_le big_page_size{}; // depends on GPU's available_big_page_sizes; 0=default
+        s32_le as_fd{};         // ignored; passes 0
+        u32_le flags{};         // passes 0
+        u32_le reserved{};      // ignored; passes 0
+        u64_le unk0{};
+        u64_le unk1{};
+        u64_le unk2{};
     };
     static_assert(sizeof(IoctlInitalizeEx) == 40, "IoctlInitalizeEx is incorrect size");
 
     struct IoctlAllocSpace {
-        u32_le pages;
-        u32_le page_size;
-        AddressSpaceFlags flags;
+        u32_le pages{};
+        u32_le page_size{};
+        AddressSpaceFlags flags{};
         INSERT_PADDING_WORDS(1);
         union {
             u64_le offset;
@@ -109,70 +100,73 @@ private:
     static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size");
 
     struct IoctlFreeSpace {
-        u64_le offset;
-        u32_le pages;
-        u32_le page_size;
+        u64_le offset{};
+        u32_le pages{};
+        u32_le page_size{};
     };
     static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size");
 
     struct IoctlRemapEntry {
-        u16_le flags;
-        u16_le kind;
-        u32_le nvmap_handle;
-        u32_le map_offset;
-        u32_le offset;
-        u32_le pages;
+        u16_le flags{};
+        u16_le kind{};
+        u32_le nvmap_handle{};
+        u32_le map_offset{};
+        u32_le offset{};
+        u32_le pages{};
     };
     static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size");
 
     struct IoctlMapBufferEx {
-        AddressSpaceFlags flags; // bit0: fixed_offset, bit2: cacheable
-        u32_le kind;             // -1 is default
-        u32_le nvmap_handle;
-        u32_le page_size; // 0 means don't care
-        s64_le buffer_offset;
-        u64_le mapping_size;
-        s64_le offset;
+        AddressSpaceFlags flags{}; // bit0: fixed_offset, bit2: cacheable
+        u32_le kind{};             // -1 is default
+        u32_le nvmap_handle{};
+        u32_le page_size{}; // 0 means don't care
+        s64_le buffer_offset{};
+        u64_le mapping_size{};
+        s64_le offset{};
     };
     static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size");
 
     struct IoctlUnmapBuffer {
-        s64_le offset;
+        s64_le offset{};
     };
     static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size");
 
     struct IoctlBindChannel {
-        u32_le fd;
+        s32_le fd{};
     };
     static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size");
 
     struct IoctlVaRegion {
-        u64_le offset;
-        u32_le page_size;
+        u64_le offset{};
+        u32_le page_size{};
         INSERT_PADDING_WORDS(1);
-        u64_le pages;
+        u64_le pages{};
     };
     static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size");
 
     struct IoctlGetVaRegions {
-        u64_le buf_addr; // (contained output user ptr on linux, ignored)
-        u32_le buf_size; // forced to 2*sizeof(struct va_region)
-        u32_le reserved;
-        IoctlVaRegion regions[2];
+        u64_le buf_addr{}; // (contained output user ptr on linux, ignored)
+        u32_le buf_size{}; // forced to 2*sizeof(struct va_region)
+        u32_le reserved{};
+        IoctlVaRegion regions[2]{};
     };
     static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2,
                   "IoctlGetVaRegions is incorrect size");
 
-    u32 channel{};
+    s32 channel{};
+
+    NvResult InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
 
-    u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 Remap(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 FreeSpace(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output,
+                          std::vector<u8>& inline_output);
 
     std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
     void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);

+ 43 - 32
src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp

@@ -20,41 +20,54 @@ nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface,
     : nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {}
 nvhost_ctrl::~nvhost_ctrl() = default;
 
-u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-                       std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-                       IoctlVersion version) {
-    LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
-              command.raw, input.size(), output.size());
-
-    switch (static_cast<IoctlCommand>(command.raw)) {
-    case IoctlCommand::IocGetConfigCommand:
-        return NvOsGetConfigU32(input, output);
-    case IoctlCommand::IocCtrlEventWaitCommand:
-        return IocCtrlEventWait(input, output, false, ctrl);
-    case IoctlCommand::IocCtrlEventWaitAsyncCommand:
-        return IocCtrlEventWait(input, output, true, ctrl);
-    case IoctlCommand::IocCtrlEventRegisterCommand:
-        return IocCtrlEventRegister(input, output);
-    case IoctlCommand::IocCtrlEventUnregisterCommand:
-        return IocCtrlEventUnregister(input, output);
-    case IoctlCommand::IocCtrlClearEventWaitCommand:
-        return IocCtrlClearEventWait(input, output);
+NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+    switch (command.group) {
+    case 0x0:
+        switch (command.cmd) {
+        case 0x1b:
+            return NvOsGetConfigU32(input, output);
+        case 0x1c:
+            return IocCtrlClearEventWait(input, output);
+        case 0x1d:
+            return IocCtrlEventWait(input, output, false);
+        case 0x1e:
+            return IocCtrlEventWait(input, output, true);
+        case 0x1f:
+            return IocCtrlEventRegister(input, output);
+        case 0x20:
+            return IocCtrlEventUnregister(input, output);
+        }
+        break;
     default:
-        UNIMPLEMENTED_MSG("Unimplemented ioctl");
-        return 0;
+        break;
     }
+
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
+
+NvResult nvhost_ctrl::Ioctl2(Ioctl command, const std::vector<u8>& input,
+                             const std::vector<u8>& inline_input, std::vector<u8>& output) {
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
+
+NvResult nvhost_ctrl::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                             std::vector<u8>& inline_output) {
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
 }
 
-u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
     IocGetConfigParams params{};
     std::memcpy(&params, input.data(), sizeof(params));
     LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(),
               params.param_str.data());
-    return 0x30006; // Returns error on production mode
+    return NvResult::ConfigVarNotFound; // Returns error on production mode
 }
 
-u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
-                                  bool is_async, IoctlCtrl& ctrl) {
+NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
+                                       bool is_async) {
     IocCtrlEventWaitParams params{};
     std::memcpy(&params, input.data(), sizeof(params));
     LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
@@ -126,10 +139,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
         params.value |= event_id;
         event.event.writable->Clear();
         gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
-        if (!is_async && ctrl.fresh_call) {
-            ctrl.must_delay = true;
-            ctrl.timeout = params.timeout;
-            ctrl.event_id = event_id;
+        if (!is_async) {
             return NvResult::Timeout;
         }
         std::memcpy(output.data(), &params, sizeof(params));
@@ -139,7 +149,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
     return NvResult::BadParameter;
 }
 
-u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
     IocCtrlEventRegisterParams params{};
     std::memcpy(&params, input.data(), sizeof(params));
     const u32 event_id = params.user_event_id & 0x00FF;
@@ -154,7 +164,8 @@ u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<
     return NvResult::Success;
 }
 
-u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input,
+                                             std::vector<u8>& output) {
     IocCtrlEventUnregisterParams params{};
     std::memcpy(&params, input.data(), sizeof(params));
     const u32 event_id = params.user_event_id & 0x00FF;
@@ -169,7 +180,7 @@ u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vecto
     return NvResult::Success;
 }
 
-u32 nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
     IocCtrlEventSignalParams params{};
     std::memcpy(&params, input.data(), sizeof(params));
 

+ 45 - 64
src/core/hle/service/nvdrv/devices/nvhost_ctrl.h

@@ -18,132 +18,113 @@ public:
                          SyncpointManager& syncpoint_manager);
     ~nvhost_ctrl() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-              std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-              IoctlVersion version) override;
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+    NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                    std::vector<u8>& inline_output) override;
 
 private:
-    enum class IoctlCommand : u32_le {
-        IocSyncptReadCommand = 0xC0080014,
-        IocSyncptIncrCommand = 0x40040015,
-        IocSyncptWaitCommand = 0xC00C0016,
-        IocModuleMutexCommand = 0x40080017,
-        IocModuleRegRDWRCommand = 0xC0180018,
-        IocSyncptWaitexCommand = 0xC0100019,
-        IocSyncptReadMaxCommand = 0xC008001A,
-        IocGetConfigCommand = 0xC183001B,
-        IocCtrlClearEventWaitCommand = 0xC004001C,
-        IocCtrlEventWaitCommand = 0xC010001D,
-        IocCtrlEventWaitAsyncCommand = 0xC010001E,
-        IocCtrlEventRegisterCommand = 0xC004001F,
-        IocCtrlEventUnregisterCommand = 0xC0040020,
-        IocCtrlEventKillCommand = 0x40080021,
-    };
     struct IocSyncptReadParams {
-        u32_le id;
-        u32_le value;
+        u32_le id{};
+        u32_le value{};
     };
     static_assert(sizeof(IocSyncptReadParams) == 8, "IocSyncptReadParams is incorrect size");
 
     struct IocSyncptIncrParams {
-        u32_le id;
+        u32_le id{};
     };
     static_assert(sizeof(IocSyncptIncrParams) == 4, "IocSyncptIncrParams is incorrect size");
 
     struct IocSyncptWaitParams {
-        u32_le id;
-        u32_le thresh;
-        s32_le timeout;
+        u32_le id{};
+        u32_le thresh{};
+        s32_le timeout{};
     };
     static_assert(sizeof(IocSyncptWaitParams) == 12, "IocSyncptWaitParams is incorrect size");
 
     struct IocModuleMutexParams {
-        u32_le id;
-        u32_le lock; // (0 = unlock and 1 = lock)
+        u32_le id{};
+        u32_le lock{}; // (0 = unlock and 1 = lock)
     };
     static_assert(sizeof(IocModuleMutexParams) == 8, "IocModuleMutexParams is incorrect size");
 
     struct IocModuleRegRDWRParams {
-        u32_le id;
-        u32_le num_offsets;
-        u32_le block_size;
-        u32_le offsets;
-        u32_le values;
-        u32_le write;
+        u32_le id{};
+        u32_le num_offsets{};
+        u32_le block_size{};
+        u32_le offsets{};
+        u32_le values{};
+        u32_le write{};
     };
     static_assert(sizeof(IocModuleRegRDWRParams) == 24, "IocModuleRegRDWRParams is incorrect size");
 
     struct IocSyncptWaitexParams {
-        u32_le id;
-        u32_le thresh;
-        s32_le timeout;
-        u32_le value;
+        u32_le id{};
+        u32_le thresh{};
+        s32_le timeout{};
+        u32_le value{};
     };
     static_assert(sizeof(IocSyncptWaitexParams) == 16, "IocSyncptWaitexParams is incorrect size");
 
     struct IocSyncptReadMaxParams {
-        u32_le id;
-        u32_le value;
+        u32_le id{};
+        u32_le value{};
     };
     static_assert(sizeof(IocSyncptReadMaxParams) == 8, "IocSyncptReadMaxParams is incorrect size");
 
     struct IocGetConfigParams {
-        std::array<char, 0x41> domain_str;
-        std::array<char, 0x41> param_str;
-        std::array<char, 0x101> config_str;
+        std::array<char, 0x41> domain_str{};
+        std::array<char, 0x41> param_str{};
+        std::array<char, 0x101> config_str{};
     };
     static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
 
     struct IocCtrlEventSignalParams {
-        u32_le event_id;
+        u32_le event_id{};
     };
     static_assert(sizeof(IocCtrlEventSignalParams) == 4,
                   "IocCtrlEventSignalParams is incorrect size");
 
     struct IocCtrlEventWaitParams {
-        u32_le syncpt_id;
-        u32_le threshold;
-        s32_le timeout;
-        u32_le value;
+        u32_le syncpt_id{};
+        u32_le threshold{};
+        s32_le timeout{};
+        u32_le value{};
     };
     static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size");
 
     struct IocCtrlEventWaitAsyncParams {
-        u32_le syncpt_id;
-        u32_le threshold;
-        u32_le timeout;
-        u32_le value;
+        u32_le syncpt_id{};
+        u32_le threshold{};
+        u32_le timeout{};
+        u32_le value{};
     };
     static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16,
                   "IocCtrlEventWaitAsyncParams is incorrect size");
 
     struct IocCtrlEventRegisterParams {
-        u32_le user_event_id;
+        u32_le user_event_id{};
     };
     static_assert(sizeof(IocCtrlEventRegisterParams) == 4,
                   "IocCtrlEventRegisterParams is incorrect size");
 
     struct IocCtrlEventUnregisterParams {
-        u32_le user_event_id;
+        u32_le user_event_id{};
     };
     static_assert(sizeof(IocCtrlEventUnregisterParams) == 4,
                   "IocCtrlEventUnregisterParams is incorrect size");
 
     struct IocCtrlEventKill {
-        u64_le user_events;
+        u64_le user_events{};
     };
     static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size");
 
-    u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
-
-    u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async,
-                         IoctlCtrl& ctrl);
-
-    u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
-
-    u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
-
-    u32 IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
+    NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
 
     EventInterface& events_interface;
     SyncpointManager& syncpoint_manager;

+ 133 - 59
src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp

@@ -15,39 +15,66 @@ namespace Service::Nvidia::Devices {
 nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}
 nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
 
-u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input,
-                           const std::vector<u8>& input2, std::vector<u8>& output,
-                           std::vector<u8>& output2, IoctlCtrl& ctrl, IoctlVersion version) {
-    LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
-              command.raw, input.size(), output.size());
-
-    switch (static_cast<IoctlCommand>(command.raw)) {
-    case IoctlCommand::IocGetCharacteristicsCommand:
-        return GetCharacteristics(input, output, output2, version);
-    case IoctlCommand::IocGetTPCMasksCommand:
-        return GetTPCMasks(input, output, output2, version);
-    case IoctlCommand::IocGetActiveSlotMaskCommand:
-        return GetActiveSlotMask(input, output);
-    case IoctlCommand::IocZcullGetCtxSizeCommand:
-        return ZCullGetCtxSize(input, output);
-    case IoctlCommand::IocZcullGetInfo:
-        return ZCullGetInfo(input, output);
-    case IoctlCommand::IocZbcSetTable:
-        return ZBCSetTable(input, output);
-    case IoctlCommand::IocZbcQueryTable:
-        return ZBCQueryTable(input, output);
-    case IoctlCommand::IocFlushL2:
-        return FlushL2(input, output);
-    case IoctlCommand::IocGetGpuTime:
-        return GetGpuTime(input, output);
+NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
+                                 std::vector<u8>& output) {
+    switch (command.group) {
+    case 'G':
+        switch (command.cmd) {
+        case 0x1:
+            return ZCullGetCtxSize(input, output);
+        case 0x2:
+            return ZCullGetInfo(input, output);
+        case 0x3:
+            return ZBCSetTable(input, output);
+        case 0x4:
+            return ZBCQueryTable(input, output);
+        case 0x5:
+            return GetCharacteristics(input, output);
+        case 0x6:
+            return GetTPCMasks(input, output);
+        case 0x7:
+            return FlushL2(input, output);
+        case 0x14:
+            return GetActiveSlotMask(input, output);
+        case 0x1c:
+            return GetGpuTime(input, output);
+        default:
+            break;
+        }
+        break;
+    }
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
+
+NvResult nvhost_ctrl_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
+                                 const std::vector<u8>& inline_input, std::vector<u8>& output) {
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
+
+NvResult nvhost_ctrl_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input,
+                                 std::vector<u8>& output, std::vector<u8>& inline_output) {
+    switch (command.group) {
+    case 'G':
+        switch (command.cmd) {
+        case 0x5:
+            return GetCharacteristics(input, output, inline_output);
+        case 0x6:
+            return GetTPCMasks(input, output, inline_output);
+        default:
+            break;
+        }
+        break;
     default:
-        UNIMPLEMENTED_MSG("Unimplemented ioctl");
-        return 0;
+        break;
     }
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
 }
 
-u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
-                                        std::vector<u8>& output2, IoctlVersion version) {
+NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input,
+                                             std::vector<u8>& output) {
     LOG_DEBUG(Service_NVDRV, "called");
     IoctlCharacteristics params{};
     std::memcpy(&params, input.data(), input.size());
@@ -88,36 +115,83 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto
     params.gc.gr_compbit_store_base_hw = 0x0;
     params.gpu_characteristics_buf_size = 0xA0;
     params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
+    std::memcpy(output.data(), &params, output.size());
+    return NvResult::Success;
+}
 
-    if (version == IoctlVersion::Version3) {
-        std::memcpy(output.data(), input.data(), output.size());
-        std::memcpy(output2.data(), &params.gc, output2.size());
-    } else {
-        std::memcpy(output.data(), &params, output.size());
-    }
-    return 0;
+NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
+                                             std::vector<u8>& inline_output) {
+    LOG_DEBUG(Service_NVDRV, "called");
+    IoctlCharacteristics params{};
+    std::memcpy(&params, input.data(), input.size());
+    params.gc.arch = 0x120;
+    params.gc.impl = 0xb;
+    params.gc.rev = 0xa1;
+    params.gc.num_gpc = 0x1;
+    params.gc.l2_cache_size = 0x40000;
+    params.gc.on_board_video_memory_size = 0x0;
+    params.gc.num_tpc_per_gpc = 0x2;
+    params.gc.bus_type = 0x20;
+    params.gc.big_page_size = 0x20000;
+    params.gc.compression_page_size = 0x20000;
+    params.gc.pde_coverage_bit_count = 0x1B;
+    params.gc.available_big_page_sizes = 0x30000;
+    params.gc.gpc_mask = 0x1;
+    params.gc.sm_arch_sm_version = 0x503;
+    params.gc.sm_arch_spa_version = 0x503;
+    params.gc.sm_arch_warp_count = 0x80;
+    params.gc.gpu_va_bit_count = 0x28;
+    params.gc.reserved = 0x0;
+    params.gc.flags = 0x55;
+    params.gc.twod_class = 0x902D;
+    params.gc.threed_class = 0xB197;
+    params.gc.compute_class = 0xB1C0;
+    params.gc.gpfifo_class = 0xB06F;
+    params.gc.inline_to_memory_class = 0xA140;
+    params.gc.dma_copy_class = 0xB0B5;
+    params.gc.max_fbps_count = 0x1;
+    params.gc.fbp_en_mask = 0x0;
+    params.gc.max_ltc_per_fbp = 0x2;
+    params.gc.max_lts_per_ltc = 0x1;
+    params.gc.max_tex_per_tpc = 0x0;
+    params.gc.max_gpc_count = 0x1;
+    params.gc.rop_l2_en_mask_0 = 0x21D70;
+    params.gc.rop_l2_en_mask_1 = 0x0;
+    params.gc.chipname = 0x6230326D67;
+    params.gc.gr_compbit_store_base_hw = 0x0;
+    params.gpu_characteristics_buf_size = 0xA0;
+    params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
+
+    std::memcpy(output.data(), input.data(), output.size());
+    std::memcpy(inline_output.data(), &params.gc, inline_output.size());
+    return NvResult::Success;
 }
 
-u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
-                                 std::vector<u8>& output2, IoctlVersion version) {
+NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlGpuGetTpcMasksArgs params{};
     std::memcpy(&params, input.data(), input.size());
     LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
     if (params.mask_buffer_size != 0) {
         params.tcp_mask = 3;
     }
+    std::memcpy(output.data(), &params, output.size());
+    return NvResult::Success;
+}
 
-    if (version == IoctlVersion::Version3) {
-        std::memcpy(output.data(), input.data(), output.size());
-        std::memcpy(output2.data(), &params.tcp_mask, output2.size());
-    } else {
-        std::memcpy(output.data(), &params, output.size());
+NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
+                                      std::vector<u8>& inline_output) {
+    IoctlGpuGetTpcMasksArgs params{};
+    std::memcpy(&params, input.data(), input.size());
+    LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
+    if (params.mask_buffer_size != 0) {
+        params.tcp_mask = 3;
     }
-
-    return 0;
+    std::memcpy(output.data(), &params, output.size());
+    std::memcpy(inline_output.data(), &params.tcp_mask, inline_output.size());
+    return NvResult::Success;
 }
 
-u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) {
     LOG_DEBUG(Service_NVDRV, "called");
 
     IoctlActiveSlotMask params{};
@@ -127,10 +201,10 @@ u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector
     params.slot = 0x07;
     params.mask = 0x01;
     std::memcpy(output.data(), &params, output.size());
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) {
     LOG_DEBUG(Service_NVDRV, "called");
 
     IoctlZcullGetCtxSize params{};
@@ -139,10 +213,10 @@ u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u
     }
     params.size = 0x1;
     std::memcpy(output.data(), &params, output.size());
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) {
     LOG_DEBUG(Service_NVDRV, "called");
 
     IoctlNvgpuGpuZcullGetInfoArgs params{};
@@ -162,47 +236,47 @@ u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>&
     params.subregion_height_align_pixels = 0x40;
     params.subregion_count = 0x10;
     std::memcpy(output.data(), &params, output.size());
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) {
     LOG_WARNING(Service_NVDRV, "(STUBBED) called");
 
     IoctlZbcSetTable params{};
     std::memcpy(&params, input.data(), input.size());
     // TODO(ogniK): What does this even actually do?
     std::memcpy(output.data(), &params, output.size());
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) {
     LOG_WARNING(Service_NVDRV, "(STUBBED) called");
 
     IoctlZbcQueryTable params{};
     std::memcpy(&params, input.data(), input.size());
     // TODO : To implement properly
     std::memcpy(output.data(), &params, output.size());
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) {
     LOG_WARNING(Service_NVDRV, "(STUBBED) called");
 
     IoctlFlushL2 params{};
     std::memcpy(&params, input.data(), input.size());
     // TODO : To implement properly
     std::memcpy(output.data(), &params, output.size());
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) {
     LOG_DEBUG(Service_NVDRV, "called");
 
     IoctlGetGpuTime params{};
     std::memcpy(&params, input.data(), input.size());
     params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count());
     std::memcpy(output.data(), &params, output.size());
-    return 0;
+    return NvResult::Success;
 }
 
 } // namespace Service::Nvidia::Devices

+ 20 - 35
src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h

@@ -16,32 +16,13 @@ public:
     explicit nvhost_ctrl_gpu(Core::System& system);
     ~nvhost_ctrl_gpu() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-              std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-              IoctlVersion version) override;
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+    NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                    std::vector<u8>& inline_output) override;
 
 private:
-    enum class IoctlCommand : u32_le {
-        IocGetCharacteristicsCommand = 0xC0B04705,
-        IocGetTPCMasksCommand = 0xC0184706,
-        IocGetActiveSlotMaskCommand = 0x80084714,
-        IocZcullGetCtxSizeCommand = 0x80044701,
-        IocZcullGetInfo = 0x80284702,
-        IocZbcSetTable = 0x402C4703,
-        IocZbcQueryTable = 0xC0344704,
-        IocFlushL2 = 0x40084707,
-        IocInvalICache = 0x4008470D,
-        IocSetMmudebugMode = 0x4008470E,
-        IocSetSmDebugMode = 0x4010470F,
-        IocWaitForPause = 0xC0084710,
-        IocGetTcpExceptionEnStatus = 0x80084711,
-        IocNumVsms = 0x80084712,
-        IocVsmsMapping = 0xC0044713,
-        IocGetErrorChannelUserData = 0xC008471B,
-        IocGetGpuTime = 0xC010471C,
-        IocGetCpuTimeCorrelationInfo = 0xC108471D,
-    };
-
     struct IoctlGpuCharacteristics {
         u32_le arch;                       // 0x120 (NVGPU_GPU_ARCH_GM200)
         u32_le impl;                       // 0xB (NVGPU_GPU_IMPL_GM20B)
@@ -159,17 +140,21 @@ private:
     };
     static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size");
 
-    u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
-                           std::vector<u8>& output2, IoctlVersion version);
-    u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, std::vector<u8>& output2,
-                    IoctlVersion version);
-    u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
+                                std::vector<u8>& inline_output);
+
+    NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
+                         std::vector<u8>& inline_output);
+
+    NvResult GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
 };
 
 } // namespace Service::Nvidia::Devices

+ 105 - 79
src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp

@@ -23,107 +23,132 @@ nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
 
 nvhost_gpu::~nvhost_gpu() = default;
 
-u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-                      std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-                      IoctlVersion version) {
-    LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
-              command.raw, input.size(), output.size());
-
-    switch (static_cast<IoctlCommand>(command.raw)) {
-    case IoctlCommand::IocSetNVMAPfdCommand:
-        return SetNVMAPfd(input, output);
-    case IoctlCommand::IocSetClientDataCommand:
-        return SetClientData(input, output);
-    case IoctlCommand::IocGetClientDataCommand:
-        return GetClientData(input, output);
-    case IoctlCommand::IocZCullBind:
-        return ZCullBind(input, output);
-    case IoctlCommand::IocSetErrorNotifierCommand:
-        return SetErrorNotifier(input, output);
-    case IoctlCommand::IocChannelSetPriorityCommand:
-        return SetChannelPriority(input, output);
-    case IoctlCommand::IocAllocGPFIFOEx2Command:
-        return AllocGPFIFOEx2(input, output);
-    case IoctlCommand::IocAllocObjCtxCommand:
-        return AllocateObjectContext(input, output);
-    case IoctlCommand::IocChannelGetWaitbaseCommand:
-        return GetWaitbase(input, output);
-    case IoctlCommand::IocChannelSetTimeoutCommand:
-        return ChannelSetTimeout(input, output);
-    case IoctlCommand::IocChannelSetTimeslice:
-        return ChannelSetTimeslice(input, output);
-    default:
+NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+    switch (command.group) {
+    case 0x0:
+        switch (command.cmd) {
+        case 0x3:
+            return GetWaitbase(input, output);
+        default:
+            break;
+        }
+        break;
+    case 'H':
+        switch (command.cmd) {
+        case 0x1:
+            return SetNVMAPfd(input, output);
+        case 0x3:
+            return ChannelSetTimeout(input, output);
+        case 0x8:
+            return SubmitGPFIFOBase(input, output, false);
+        case 0x9:
+            return AllocateObjectContext(input, output);
+        case 0xb:
+            return ZCullBind(input, output);
+        case 0xc:
+            return SetErrorNotifier(input, output);
+        case 0xd:
+            return SetChannelPriority(input, output);
+        case 0x1a:
+            return AllocGPFIFOEx2(input, output);
+        case 0x1b:
+            return SubmitGPFIFOBase(input, output, true);
+        case 0x1d:
+            return ChannelSetTimeslice(input, output);
+        default:
+            break;
+        }
+        break;
+    case 'G':
+        switch (command.cmd) {
+        case 0x14:
+            return SetClientData(input, output);
+        case 0x15:
+            return GetClientData(input, output);
+        default:
+            break;
+        }
         break;
     }
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+};
 
-    if (command.group == NVGPU_IOCTL_MAGIC) {
-        if (command.cmd == NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO) {
-            return SubmitGPFIFO(input, output);
-        }
-        if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) {
-            return KickoffPB(input, output, input2, version);
+NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
+                            const std::vector<u8>& inline_input, std::vector<u8>& output) {
+    switch (command.group) {
+    case 'H':
+        switch (command.cmd) {
+        case 0x1b:
+            return SubmitGPFIFOBase(input, inline_input, output);
         }
+        break;
     }
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
 
-    UNIMPLEMENTED_MSG("Unimplemented ioctl");
-    return 0;
-};
+NvResult nvhost_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                            std::vector<u8>& inline_output) {
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
 
-u32 nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlSetNvmapFD params{};
     std::memcpy(&params, input.data(), input.size());
     LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
 
     nvmap_fd = params.nvmap_fd;
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
     LOG_DEBUG(Service_NVDRV, "called");
 
     IoctlClientData params{};
     std::memcpy(&params, input.data(), input.size());
     user_data = params.data;
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
     LOG_DEBUG(Service_NVDRV, "called");
 
     IoctlClientData params{};
     std::memcpy(&params, input.data(), input.size());
     params.data = user_data;
     std::memcpy(output.data(), &params, output.size());
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) {
     std::memcpy(&zcull_params, input.data(), input.size());
     LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va,
               zcull_params.mode);
 
     std::memcpy(output.data(), &zcull_params, output.size());
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlSetErrorNotifier params{};
     std::memcpy(&params, input.data(), input.size());
     LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset,
                 params.size, params.mem);
 
     std::memcpy(output.data(), &params, output.size());
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) {
     std::memcpy(&channel_priority, input.data(), input.size());
     LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
 
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlAllocGpfifoEx2 params{};
     std::memcpy(&params, input.data(), input.size());
     LOG_WARNING(Service_NVDRV,
@@ -137,10 +162,10 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
     params.fence_out = channel_fence;
 
     std::memcpy(output.data(), &params, output.size());
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlAllocObjCtx params{};
     std::memcpy(&params, input.data(), input.size());
     LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num,
@@ -148,7 +173,7 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
 
     params.obj_id = 0x0;
     std::memcpy(output.data(), &params, output.size());
-    return 0;
+    return NvResult::Success;
 }
 
 static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
@@ -192,8 +217,8 @@ static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence
     return result;
 }
 
-u32 nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
-                                 Tegra::CommandList&& entries) {
+NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
+                                      Tegra::CommandList&& entries) {
     LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
               params.num_entries, params.flags.raw);
 
@@ -227,69 +252,70 @@ u32 nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& out
     }
 
     std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output,
+                                      bool kickoff) {
     if (input.size() < sizeof(IoctlSubmitGpfifo)) {
         UNIMPLEMENTED();
+        return NvResult::InvalidSize;
     }
     IoctlSubmitGpfifo params{};
     std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
-
     Tegra::CommandList entries(params.num_entries);
-    std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)],
-                params.num_entries * sizeof(Tegra::CommandListHeader));
+
+    if (kickoff) {
+        system.Memory().ReadBlock(params.address, entries.command_lists.data(),
+                                  params.num_entries * sizeof(Tegra::CommandListHeader));
+    } else {
+        std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)],
+                    params.num_entries * sizeof(Tegra::CommandListHeader));
+    }
 
     return SubmitGPFIFOImpl(params, output, std::move(entries));
 }
 
-u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
-                          const std::vector<u8>& input2, IoctlVersion version) {
+NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input,
+                                      const std::vector<u8>& input_inline,
+                                      std::vector<u8>& output) {
     if (input.size() < sizeof(IoctlSubmitGpfifo)) {
         UNIMPLEMENTED();
+        return NvResult::InvalidSize;
     }
     IoctlSubmitGpfifo params{};
     std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
-
     Tegra::CommandList entries(params.num_entries);
-    if (version == IoctlVersion::Version2) {
-        std::memcpy(entries.command_lists.data(), input2.data(),
-                    params.num_entries * sizeof(Tegra::CommandListHeader));
-    } else {
-        system.Memory().ReadBlock(params.address, entries.command_lists.data(),
-                                  params.num_entries * sizeof(Tegra::CommandListHeader));
-    }
-
+    std::memcpy(entries.command_lists.data(), input_inline.data(), input_inline.size());
     return SubmitGPFIFOImpl(params, output, std::move(entries));
 }
 
-u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlGetWaitbase params{};
     std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
     LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
 
     params.value = 0; // Seems to be hard coded at 0
     std::memcpy(output.data(), &params, output.size());
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlChannelSetTimeout params{};
     std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout));
     LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
 
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlSetTimeslice params{};
     std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice));
     LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice);
 
     channel_timeslice = params.timeslice;
 
-    return 0;
+    return NvResult::Success;
 }
 
 } // namespace Service::Nvidia::Devices

+ 65 - 88
src/core/hle/service/nvdrv/devices/nvhost_gpu.h

@@ -20,43 +20,19 @@ class SyncpointManager;
 namespace Service::Nvidia::Devices {
 
 class nvmap;
-constexpr u32 NVGPU_IOCTL_MAGIC('H');
-constexpr u32 NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO(0x8);
-constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
-
 class nvhost_gpu final : public nvdevice {
 public:
     explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
                         SyncpointManager& syncpoint_manager);
     ~nvhost_gpu() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-              std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-              IoctlVersion version) override;
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+    NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                    std::vector<u8>& inline_output) override;
 
 private:
-    enum class IoctlCommand : u32_le {
-        IocSetNVMAPfdCommand = 0x40044801,
-        IocAllocGPFIFOCommand = 0x40084805,
-        IocSetClientDataCommand = 0x40084714,
-        IocGetClientDataCommand = 0x80084715,
-        IocZCullBind = 0xc010480b,
-        IocSetErrorNotifierCommand = 0xC018480C,
-        IocChannelSetPriorityCommand = 0x4004480D,
-        IocEnableCommand = 0x0000480E,
-        IocDisableCommand = 0x0000480F,
-        IocPreemptCommand = 0x00004810,
-        IocForceResetCommand = 0x00004811,
-        IocEventIdControlCommand = 0x40084812,
-        IocGetErrorNotificationCommand = 0xC0104817,
-        IocAllocGPFIFOExCommand = 0x40204818,
-        IocAllocGPFIFOEx2Command = 0xC020481A,
-        IocAllocObjCtxCommand = 0xC0104809,
-        IocChannelGetWaitbaseCommand = 0xC0080003,
-        IocChannelSetTimeoutCommand = 0x40044803,
-        IocChannelSetTimeslice = 0xC004481D,
-    };
-
     enum class CtxObjects : u32_le {
         Ctx2D = 0x902D,
         Ctx3D = 0xB197,
@@ -67,63 +43,63 @@ private:
     };
 
     struct IoctlSetNvmapFD {
-        u32_le nvmap_fd;
+        s32_le nvmap_fd{};
     };
     static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
 
     struct IoctlChannelSetTimeout {
-        u32_le timeout;
+        u32_le timeout{};
     };
     static_assert(sizeof(IoctlChannelSetTimeout) == 4, "IoctlChannelSetTimeout is incorrect size");
 
     struct IoctlAllocGPFIFO {
-        u32_le num_entries;
-        u32_le flags;
+        u32_le num_entries{};
+        u32_le flags{};
     };
     static_assert(sizeof(IoctlAllocGPFIFO) == 8, "IoctlAllocGPFIFO is incorrect size");
 
     struct IoctlClientData {
-        u64_le data;
+        u64_le data{};
     };
     static_assert(sizeof(IoctlClientData) == 8, "IoctlClientData is incorrect size");
 
     struct IoctlZCullBind {
-        u64_le gpu_va;
-        u32_le mode; // 0=global, 1=no_ctxsw, 2=separate_buffer, 3=part_of_regular_buf
+        u64_le gpu_va{};
+        u32_le mode{}; // 0=global, 1=no_ctxsw, 2=separate_buffer, 3=part_of_regular_buf
         INSERT_PADDING_WORDS(1);
     };
     static_assert(sizeof(IoctlZCullBind) == 16, "IoctlZCullBind is incorrect size");
 
     struct IoctlSetErrorNotifier {
-        u64_le offset;
-        u64_le size;
-        u32_le mem; // nvmap object handle
+        u64_le offset{};
+        u64_le size{};
+        u32_le mem{}; // nvmap object handle
         INSERT_PADDING_WORDS(1);
     };
     static_assert(sizeof(IoctlSetErrorNotifier) == 24, "IoctlSetErrorNotifier is incorrect size");
 
     struct IoctlChannelSetPriority {
-        u32_le priority;
+        u32_le priority{};
     };
     static_assert(sizeof(IoctlChannelSetPriority) == 4,
                   "IoctlChannelSetPriority is incorrect size");
 
     struct IoctlSetTimeslice {
-        u32_le timeslice;
+        u32_le timeslice{};
     };
     static_assert(sizeof(IoctlSetTimeslice) == 4, "IoctlSetTimeslice is incorrect size");
 
     struct IoctlEventIdControl {
-        u32_le cmd; // 0=disable, 1=enable, 2=clear
-        u32_le id;
+        u32_le cmd{}; // 0=disable, 1=enable, 2=clear
+        u32_le id{};
     };
     static_assert(sizeof(IoctlEventIdControl) == 8, "IoctlEventIdControl is incorrect size");
 
     struct IoctlGetErrorNotification {
-        u64_le timestamp;
-        u32_le info32;
-        u16_le info16;
-        u16_le status; // always 0xFFFF
+        u64_le timestamp{};
+        u32_le info32{};
+        u16_le info16{};
+        u16_le status{}; // always 0xFFFF
     };
     static_assert(sizeof(IoctlGetErrorNotification) == 16,
                   "IoctlGetErrorNotification is incorrect size");
@@ -131,39 +107,39 @@ private:
     static_assert(sizeof(Fence) == 8, "Fence is incorrect size");
 
     struct IoctlAllocGpfifoEx {
-        u32_le num_entries;
-        u32_le flags;
-        u32_le unk0;
-        u32_le unk1;
-        u32_le unk2;
-        u32_le unk3;
-        u32_le unk4;
-        u32_le unk5;
+        u32_le num_entries{};
+        u32_le flags{};
+        u32_le unk0{};
+        u32_le unk1{};
+        u32_le unk2{};
+        u32_le unk3{};
+        u32_le unk4{};
+        u32_le unk5{};
     };
     static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size");
 
     struct IoctlAllocGpfifoEx2 {
-        u32_le num_entries; // in
-        u32_le flags;       // in
-        u32_le unk0;        // in (1 works)
-        Fence fence_out;    // out
-        u32_le unk1;        // in
-        u32_le unk2;        // in
-        u32_le unk3;        // in
+        u32_le num_entries{}; // in
+        u32_le flags{};       // in
+        u32_le unk0{};        // in (1 works)
+        Fence fence_out{};    // out
+        u32_le unk1{};        // in
+        u32_le unk2{};        // in
+        u32_le unk3{};        // in
     };
     static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size");
 
     struct IoctlAllocObjCtx {
-        u32_le class_num; // 0x902D=2d, 0xB197=3d, 0xB1C0=compute, 0xA140=kepler, 0xB0B5=DMA,
-                          // 0xB06F=channel_gpfifo
-        u32_le flags;
-        u64_le obj_id; // (ignored) used for FREE_OBJ_CTX ioctl, which is not supported
+        u32_le class_num{}; // 0x902D=2d, 0xB197=3d, 0xB1C0=compute, 0xA140=kepler, 0xB0B5=DMA,
+                            // 0xB06F=channel_gpfifo
+        u32_le flags{};
+        u64_le obj_id{}; // (ignored) used for FREE_OBJ_CTX ioctl, which is not supported
     };
     static_assert(sizeof(IoctlAllocObjCtx) == 16, "IoctlAllocObjCtx is incorrect size");
 
     struct IoctlSubmitGpfifo {
-        u64_le address;     // pointer to gpfifo entry structs
-        u32_le num_entries; // number of fence objects being submitted
+        u64_le address{};     // pointer to gpfifo entry structs
+        u32_le num_entries{}; // number of fence objects being submitted
         union {
             u32_le raw;
             BitField<0, 1, u32_le> add_wait;      // append a wait sync_point to the list
@@ -172,7 +148,7 @@ private:
             BitField<4, 1, u32_le> suppress_wfi;  // suppress wait for interrupt
             BitField<8, 1, u32_le> increment;     // increment the returned fence
         } flags;
-        Fence fence_out; // returned new fence object for others to wait on
+        Fence fence_out{}; // returned new fence object for others to wait on
 
         u32 AddIncrementValue() const {
             return flags.add_increment.Value() << 1;
@@ -182,33 +158,34 @@ private:
                   "IoctlSubmitGpfifo is incorrect size");
 
     struct IoctlGetWaitbase {
-        u32 unknown; // seems to be ignored? Nintendo added this
-        u32 value;
+        u32 unknown{}; // seems to be ignored? Nintendo added this
+        u32 value{};
     };
     static_assert(sizeof(IoctlGetWaitbase) == 8, "IoctlGetWaitbase is incorrect size");
 
-    u32_le nvmap_fd{};
+    s32_le nvmap_fd{};
     u64_le user_data{};
     IoctlZCullBind zcull_params{};
     u32_le channel_priority{};
     u32_le channel_timeslice{};
 
-    u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 SetClientData(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 GetClientData(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 ZCullBind(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
-                         Tegra::CommandList&& entries);
-    u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
-                  const std::vector<u8>& input2, IoctlVersion version);
-    u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult SetClientData(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult GetClientData(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult ZCullBind(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
+                              Tegra::CommandList&& entries);
+    NvResult SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output,
+                              bool kickoff = false);
+    NvResult SubmitGPFIFOBase(const std::vector<u8>& input, const std::vector<u8>& input_inline,
+                              std::vector<u8>& output);
+    NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
 
     std::shared_ptr<nvmap> nvmap_dev;
     SyncpointManager& syncpoint_manager;

+ 50 - 38
src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp

@@ -15,46 +15,58 @@ nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_de
     : nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
 nvhost_nvdec::~nvhost_nvdec() = default;
 
-u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-                        std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-                        IoctlVersion version) {
-    LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
-              command.raw, input.size(), output.size());
-
-    switch (static_cast<IoctlCommand>(command.raw)) {
-    case IoctlCommand::IocSetNVMAPfdCommand:
-        return SetNVMAPfd(input);
-    case IoctlCommand::IocSubmit:
-        return Submit(input, output);
-    case IoctlCommand::IocGetSyncpoint:
-        return GetSyncpoint(input, output);
-    case IoctlCommand::IocGetWaitbase:
-        return GetWaitbase(input, output);
-    case IoctlCommand::IocMapBuffer:
-    case IoctlCommand::IocMapBuffer2:
-    case IoctlCommand::IocMapBuffer3:
-    case IoctlCommand::IocMapBufferEx:
-        return MapBuffer(input, output);
-    case IoctlCommand::IocUnmapBufferEx: {
-        // This command is sent when the video stream has ended, flush all video contexts
-        // This is usually sent in the folowing order: vic, nvdec, vic.
-        // Inform the GPU to clear any remaining nvdec buffers when this is detected.
-        LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
-        Tegra::ChCommandHeaderList cmdlist(1);
-        cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F};
-        system.GPU().PushCommandBuffer(cmdlist);
-        [[fallthrough]]; // fallthrough to unmap buffers
-    };
-    case IoctlCommand::IocUnmapBuffer:
-    case IoctlCommand::IocUnmapBuffer2:
-    case IoctlCommand::IocUnmapBuffer3:
-        return UnmapBuffer(input, output);
-    case IoctlCommand::IocSetSubmitTimeout:
-        return SetSubmitTimeout(input, output);
+NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input,
+                              std::vector<u8>& output) {
+    switch (command.group) {
+    case 0x0:
+        switch (command.cmd) {
+        case 0x1:
+            return Submit(input, output);
+        case 0x2:
+            return GetSyncpoint(input, output);
+        case 0x3:
+            return GetWaitbase(input, output);
+        case 0x7:
+            return SetSubmitTimeout(input, output);
+        case 0x9:
+            return MapBuffer(input, output);
+        case 0xa: {
+            if (command.length == 0x1c) {
+                LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
+                Tegra::ChCommandHeaderList cmdlist(1);
+                cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F};
+                system.GPU().PushCommandBuffer(cmdlist);
+            }
+            return UnmapBuffer(input, output);
+        }
+        default:
+            break;
+        }
+        break;
+    case 'H':
+        switch (command.cmd) {
+        case 0x1:
+            return SetNVMAPfd(input);
+        default:
+            break;
+        }
+        break;
     }
 
-    UNIMPLEMENTED_MSG("Unimplemented ioctl 0x{:X}", command.raw);
-    return 0;
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
+
+NvResult nvhost_nvdec::Ioctl2(Ioctl command, const std::vector<u8>& input,
+                              const std::vector<u8>& inline_input, std::vector<u8>& output) {
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
+
+NvResult nvhost_nvdec::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                              std::vector<u8>& inline_output) {
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
 }
 
 } // namespace Service::Nvidia::Devices

+ 5 - 20
src/core/hle/service/nvdrv/devices/nvhost_nvdec.h

@@ -14,26 +14,11 @@ public:
     explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
     ~nvhost_nvdec() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-              std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-              IoctlVersion version) override;
-
-private:
-    enum class IoctlCommand : u32_le {
-        IocSetNVMAPfdCommand = 0x40044801,
-        IocSubmit = 0xC0400001,
-        IocGetSyncpoint = 0xC0080002,
-        IocGetWaitbase = 0xC0080003,
-        IocMapBuffer = 0xC01C0009,
-        IocMapBuffer2 = 0xC16C0009,
-        IocMapBuffer3 = 0xC15C0009,
-        IocMapBufferEx = 0xC0A40009,
-        IocUnmapBuffer = 0xC0A4000A,
-        IocUnmapBuffer2 = 0xC16C000A,
-        IocUnmapBufferEx = 0xC01C000A,
-        IocUnmapBuffer3 = 0xC15C000A,
-        IocSetSubmitTimeout = 0x40040007,
-    };
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+    NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                    std::vector<u8>& inline_output) override;
 };
 
 } // namespace Service::Nvidia::Devices

+ 19 - 24
src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp

@@ -36,26 +36,20 @@ std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::s
 }
 } // Anonymous namespace
 
-namespace NvErrCodes {
-constexpr u32 Success{};
-[[maybe_unused]] constexpr u32 OutOfMemory{static_cast<u32>(-12)};
-constexpr u32 InvalidInput{static_cast<u32>(-22)};
-} // namespace NvErrCodes
-
 nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
     : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
 nvhost_nvdec_common::~nvhost_nvdec_common() = default;
 
-u32 nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
+NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
     IoctlSetNvmapFD params{};
     std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
     LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
 
     nvmap_fd = params.nvmap_fd;
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlSubmit params{};
     std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
     LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
@@ -83,12 +77,12 @@ u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& o
 
     for (const auto& cmd_buffer : command_buffers) {
         auto object = nvmap_dev->GetObject(cmd_buffer.memory_id);
-        ASSERT_OR_EXECUTE(object, return NvErrCodes::InvalidInput;);
+        ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
         const auto map = FindBufferMap(object->dma_map_addr);
         if (!map) {
             LOG_ERROR(Service_NVDRV, "Tried to submit an invalid offset 0x{:X} dma 0x{:X}",
                       object->addr, object->dma_map_addr);
-            return 0;
+            return NvResult::Success;
         }
         Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
         gpu.MemoryManager().ReadBlock(map->StartAddr() + cmd_buffer.offset, cmdlist.data(),
@@ -105,10 +99,10 @@ u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& o
     offset = WriteVectors(output, syncpt_increments, offset);
     offset = WriteVectors(output, wait_checks, offset);
 
-    return NvErrCodes::Success;
+    return NvResult::Success;
 }
 
-u32 nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlGetSyncpoint params{};
     std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
     LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
@@ -118,18 +112,18 @@ u32 nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<
     params.value = 0;
     std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
 
-    return NvErrCodes::Success;
+    return NvResult::Success;
 }
 
-u32 nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlGetWaitbase params{};
     std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
     params.value = 0; // Seems to be hard coded at 0
     std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlMapBuffer params{};
     std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
     std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
@@ -143,7 +137,7 @@ u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>
         if (!object) {
             LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
             std::memcpy(output.data(), &params, output.size());
-            return NvErrCodes::InvalidInput;
+            return NvResult::InvalidState;
         }
         if (object->dma_map_addr == 0) {
             // NVDEC and VIC memory is in the 32-bit address space
@@ -165,10 +159,10 @@ u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>
     std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(),
                 cmd_buffer_handles.size() * sizeof(MapBufferEntry));
 
-    return NvErrCodes::Success;
+    return NvResult::Success;
 }
 
-u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlMapBuffer params{};
     std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
     std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
@@ -181,7 +175,7 @@ u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u
         if (!object) {
             LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
             std::memcpy(output.data(), &params, output.size());
-            return NvErrCodes::InvalidInput;
+            return NvResult::InvalidState;
         }
         if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) {
             gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
@@ -193,13 +187,14 @@ u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u
         object->dma_map_addr = 0;
     }
     std::memset(output.data(), 0, output.size());
-    return NvErrCodes::Success;
+    return NvResult::Success;
 }
 
-u32 nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input,
+                                               std::vector<u8>& output) {
     std::memcpy(&submit_timeout, input.data(), input.size());
     LOG_WARNING(Service_NVDRV, "(STUBBED) called");
-    return NvErrCodes::Success;
+    return NvResult::Success;
 }
 
 std::optional<nvhost_nvdec_common::BufferMap> nvhost_nvdec_common::FindBufferMap(

+ 69 - 41
src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h

@@ -18,9 +18,37 @@ public:
     explicit nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
     ~nvhost_nvdec_common() override;
 
-    virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-                      std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-                      IoctlVersion version) = 0;
+    /**
+     * Handles an ioctl1 request.
+     * @param command The ioctl command id.
+     * @param input A buffer containing the input data for the ioctl.
+     * @param output A buffer where the output data will be written to.
+     * @returns The result code of the ioctl.
+     */
+    virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input,
+                            std::vector<u8>& output) = 0;
+
+    /**
+     * Handles an ioctl2 request.
+     * @param command The ioctl command id.
+     * @param input A buffer containing the input data for the ioctl.
+     * @param inline_input A buffer containing the input data for the ioctl which has been inlined.
+     * @param output A buffer where the output data will be written to.
+     * @returns The result code of the ioctl.
+     */
+    virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+                            const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
+
+    /**
+     * Handles an ioctl3 request.
+     * @param command The ioctl command id.
+     * @param input A buffer containing the input data for the ioctl.
+     * @param output A buffer where the output data will be written to.
+     * @param inline_output A buffer where the inlined output data will be written to.
+     * @returns The result code of the ioctl.
+     */
+    virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                            std::vector<u8>& inline_output) = 0;
 
 protected:
     class BufferMap final {
@@ -63,102 +91,102 @@ protected:
     };
 
     struct IoctlSetNvmapFD {
-        u32_le nvmap_fd;
+        s32_le nvmap_fd{};
     };
     static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
 
     struct IoctlSubmitCommandBuffer {
-        u32_le id;
-        u32_le offset;
-        u32_le count;
+        u32_le id{};
+        u32_le offset{};
+        u32_le count{};
     };
     static_assert(sizeof(IoctlSubmitCommandBuffer) == 0xC,
                   "IoctlSubmitCommandBuffer is incorrect size");
     struct IoctlSubmit {
-        u32_le cmd_buffer_count;
-        u32_le relocation_count;
-        u32_le syncpoint_count;
-        u32_le fence_count;
+        u32_le cmd_buffer_count{};
+        u32_le relocation_count{};
+        u32_le syncpoint_count{};
+        u32_le fence_count{};
     };
     static_assert(sizeof(IoctlSubmit) == 0x10, "IoctlSubmit has incorrect size");
 
     struct CommandBuffer {
-        s32 memory_id;
-        u32 offset;
-        s32 word_count;
+        s32 memory_id{};
+        u32 offset{};
+        s32 word_count{};
     };
     static_assert(sizeof(CommandBuffer) == 0xC, "CommandBuffer has incorrect size");
 
     struct Reloc {
-        s32 cmdbuffer_memory;
-        s32 cmdbuffer_offset;
-        s32 target;
-        s32 target_offset;
+        s32 cmdbuffer_memory{};
+        s32 cmdbuffer_offset{};
+        s32 target{};
+        s32 target_offset{};
     };
     static_assert(sizeof(Reloc) == 0x10, "CommandBuffer has incorrect size");
 
     struct SyncptIncr {
-        u32 id;
-        u32 increments;
+        u32 id{};
+        u32 increments{};
     };
     static_assert(sizeof(SyncptIncr) == 0x8, "CommandBuffer has incorrect size");
 
     struct Fence {
-        u32 id;
-        u32 value;
+        u32 id{};
+        u32 value{};
     };
     static_assert(sizeof(Fence) == 0x8, "CommandBuffer has incorrect size");
 
     struct IoctlGetSyncpoint {
         // Input
-        u32_le param;
+        u32_le param{};
         // Output
-        u32_le value;
+        u32_le value{};
     };
     static_assert(sizeof(IoctlGetSyncpoint) == 8, "IocGetIdParams has wrong size");
 
     struct IoctlGetWaitbase {
-        u32_le unknown; // seems to be ignored? Nintendo added this
-        u32_le value;
+        u32_le unknown{}; // seems to be ignored? Nintendo added this
+        u32_le value{};
     };
     static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size");
 
     struct IoctlMapBuffer {
-        u32_le num_entries;
-        u32_le data_address; // Ignored by the driver.
-        u32_le attach_host_ch_das;
+        u32_le num_entries{};
+        u32_le data_address{}; // Ignored by the driver.
+        u32_le attach_host_ch_das{};
     };
     static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
 
     struct IocGetIdParams {
         // Input
-        u32_le param;
+        u32_le param{};
         // Output
-        u32_le value;
+        u32_le value{};
     };
     static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
 
     // Used for mapping and unmapping command buffers
     struct MapBufferEntry {
-        u32_le map_handle;
-        u32_le map_address;
+        u32_le map_handle{};
+        u32_le map_address{};
     };
     static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
 
     /// Ioctl command implementations
-    u32 SetNVMAPfd(const std::vector<u8>& input);
-    u32 Submit(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult SetNVMAPfd(const std::vector<u8>& input);
+    NvResult Submit(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
 
     std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
     void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
     std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
 
-    u32_le nvmap_fd{};
+    s32_le nvmap_fd{};
     u32_le submit_timeout{};
     std::shared_ptr<nvmap> nvmap_dev;
 

+ 29 - 13
src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp

@@ -13,28 +13,44 @@ namespace Service::Nvidia::Devices {
 nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
 nvhost_nvjpg::~nvhost_nvjpg() = default;
 
-u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-                        std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-                        IoctlVersion version) {
-    LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
-              command.raw, input.size(), output.size());
-
-    switch (static_cast<IoctlCommand>(command.raw)) {
-    case IoctlCommand::IocSetNVMAPfdCommand:
-        return SetNVMAPfd(input, output);
+NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input,
+                              std::vector<u8>& output) {
+    switch (command.group) {
+    case 'H':
+        switch (command.cmd) {
+        case 0x1:
+            return SetNVMAPfd(input, output);
+        default:
+            break;
+        }
+        break;
+    default:
+        break;
     }
 
-    UNIMPLEMENTED_MSG("Unimplemented ioctl");
-    return 0;
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
 }
 
-u32 nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_nvjpg::Ioctl2(Ioctl command, const std::vector<u8>& input,
+                              const std::vector<u8>& inline_input, std::vector<u8>& output) {
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
+
+NvResult nvhost_nvjpg::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                              std::vector<u8>& inline_output) {
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
+
+NvResult nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
     IoctlSetNvmapFD params{};
     std::memcpy(&params, input.data(), input.size());
     LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
 
     nvmap_fd = params.nvmap_fd;
-    return 0;
+    return NvResult::Success;
 }
 
 } // namespace Service::Nvidia::Devices

+ 8 - 10
src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h

@@ -16,23 +16,21 @@ public:
     explicit nvhost_nvjpg(Core::System& system);
     ~nvhost_nvjpg() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-              std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-              IoctlVersion version) override;
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+    NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                    std::vector<u8>& inline_output) override;
 
 private:
-    enum class IoctlCommand : u32_le {
-        IocSetNVMAPfdCommand = 0x40044801,
-    };
-
     struct IoctlSetNvmapFD {
-        u32_le nvmap_fd;
+        s32_le nvmap_fd{};
     };
     static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
 
-    u32_le nvmap_fd{};
+    s32_le nvmap_fd{};
 
-    u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
 };
 
 } // namespace Service::Nvidia::Devices

+ 42 - 28
src/core/hle/service/nvdrv/devices/nvhost_vic.cpp

@@ -15,36 +15,50 @@ nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
 
 nvhost_vic::~nvhost_vic() = default;
 
-u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-                      std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-                      IoctlVersion version) {
-    LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
-              command.raw, input.size(), output.size());
-
-    switch (static_cast<IoctlCommand>(command.raw)) {
-    case IoctlCommand::IocSetNVMAPfdCommand:
-        return SetNVMAPfd(input);
-    case IoctlCommand::IocSubmit:
-        return Submit(input, output);
-    case IoctlCommand::IocGetSyncpoint:
-        return GetSyncpoint(input, output);
-    case IoctlCommand::IocGetWaitbase:
-        return GetWaitbase(input, output);
-    case IoctlCommand::IocMapBuffer:
-    case IoctlCommand::IocMapBuffer2:
-    case IoctlCommand::IocMapBuffer3:
-    case IoctlCommand::IocMapBuffer4:
-    case IoctlCommand::IocMapBufferEx:
-        return MapBuffer(input, output);
-    case IoctlCommand::IocUnmapBuffer:
-    case IoctlCommand::IocUnmapBuffer2:
-    case IoctlCommand::IocUnmapBuffer3:
-    case IoctlCommand::IocUnmapBufferEx:
-        return UnmapBuffer(input, output);
+NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+    switch (command.group) {
+    case 0x0:
+        switch (command.cmd) {
+        case 0x1:
+            return Submit(input, output);
+        case 0x2:
+            return GetSyncpoint(input, output);
+        case 0x3:
+            return GetWaitbase(input, output);
+        case 0x9:
+            return MapBuffer(input, output);
+        case 0xa:
+            return UnmapBuffer(input, output);
+        default:
+            break;
+        }
+        break;
+    case 'H':
+        switch (command.cmd) {
+        case 0x1:
+            return SetNVMAPfd(input);
+        default:
+            break;
+        }
+        break;
+    default:
+        break;
     }
 
-    UNIMPLEMENTED_MSG("Unimplemented ioctl 0x{:X}", command.raw);
-    return 0;
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
+
+NvResult nvhost_vic::Ioctl2(Ioctl command, const std::vector<u8>& input,
+                            const std::vector<u8>& inline_input, std::vector<u8>& output) {
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
+
+NvResult nvhost_vic::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                            std::vector<u8>& inline_output) {
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
 }
 
 } // namespace Service::Nvidia::Devices

+ 5 - 19
src/core/hle/service/nvdrv/devices/nvhost_vic.h

@@ -13,25 +13,11 @@ class nvhost_vic final : public nvhost_nvdec_common {
 public:
     explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
     ~nvhost_vic();
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-              std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-              IoctlVersion version) override;
 
-private:
-    enum class IoctlCommand : u32_le {
-        IocSetNVMAPfdCommand = 0x40044801,
-        IocSubmit = 0xC0400001,
-        IocGetSyncpoint = 0xC0080002,
-        IocGetWaitbase = 0xC0080003,
-        IocMapBuffer = 0xC01C0009,
-        IocMapBuffer2 = 0xC0340009,
-        IocMapBuffer3 = 0xC0140009,
-        IocMapBuffer4 = 0xC00C0009,
-        IocMapBufferEx = 0xC03C0009,
-        IocUnmapBuffer = 0xC03C000A,
-        IocUnmapBuffer2 = 0xC034000A,
-        IocUnmapBuffer3 = 0xC00C000A,
-        IocUnmapBufferEx = 0xC01C000A,
-    };
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+    NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                    std::vector<u8>& inline_output) override;
 };
 } // namespace Service::Nvidia::Devices

+ 65 - 54
src/core/hle/service/nvdrv/devices/nvmap.cpp

@@ -11,13 +11,6 @@
 
 namespace Service::Nvidia::Devices {
 
-namespace NvErrCodes {
-enum {
-    OperationNotPermitted = -1,
-    InvalidValue = -22,
-};
-}
-
 nvmap::nvmap(Core::System& system) : nvdevice(system) {
     // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to
     // represent this.
@@ -26,6 +19,46 @@ nvmap::nvmap(Core::System& system) : nvdevice(system) {
 
 nvmap::~nvmap() = default;
 
+NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+    switch (command.group) {
+    case 0x1:
+        switch (command.cmd) {
+        case 0x1:
+            return IocCreate(input, output);
+        case 0x3:
+            return IocFromId(input, output);
+        case 0x4:
+            return IocAlloc(input, output);
+        case 0x5:
+            return IocFree(input, output);
+        case 0x9:
+            return IocParam(input, output);
+        case 0xe:
+            return IocGetId(input, output);
+        default:
+            break;
+        }
+        break;
+    default:
+        break;
+    }
+
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
+
+NvResult nvmap::Ioctl2(Ioctl command, const std::vector<u8>& input,
+                       const std::vector<u8>& inline_input, std::vector<u8>& output) {
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
+
+NvResult nvmap::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                       std::vector<u8>& inline_output) {
+    UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+    return NvResult::NotImplemented;
+}
+
 VAddr nvmap::GetObjectAddress(u32 handle) const {
     auto object = GetObject(handle);
     ASSERT(object);
@@ -33,28 +66,6 @@ VAddr nvmap::GetObjectAddress(u32 handle) const {
     return object->addr;
 }
 
-u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-                 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-                 IoctlVersion version) {
-    switch (static_cast<IoctlCommand>(command.raw)) {
-    case IoctlCommand::Create:
-        return IocCreate(input, output);
-    case IoctlCommand::Alloc:
-        return IocAlloc(input, output);
-    case IoctlCommand::GetId:
-        return IocGetId(input, output);
-    case IoctlCommand::FromId:
-        return IocFromId(input, output);
-    case IoctlCommand::Param:
-        return IocParam(input, output);
-    case IoctlCommand::Free:
-        return IocFree(input, output);
-    }
-
-    UNIMPLEMENTED_MSG("Unimplemented ioctl");
-    return 0;
-}
-
 u32 nvmap::CreateObject(u32 size) {
     // Create a new nvmap object and obtain a handle to it.
     auto object = std::make_shared<Object>();
@@ -70,35 +81,35 @@ u32 nvmap::CreateObject(u32 size) {
     return handle;
 }
 
-u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
     IocCreateParams params;
     std::memcpy(&params, input.data(), sizeof(params));
     LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
 
     if (!params.size) {
         LOG_ERROR(Service_NVDRV, "Size is 0");
-        return static_cast<u32>(NvErrCodes::InvalidValue);
+        return NvResult::BadValue;
     }
 
     params.handle = CreateObject(params.size);
 
     std::memcpy(output.data(), &params, sizeof(params));
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
     IocAllocParams params;
     std::memcpy(&params, input.data(), sizeof(params));
     LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
 
     if (!params.handle) {
         LOG_ERROR(Service_NVDRV, "Handle is 0");
-        return static_cast<u32>(NvErrCodes::InvalidValue);
+        return NvResult::BadValue;
     }
 
     if ((params.align - 1) & params.align) {
         LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align);
-        return static_cast<u32>(NvErrCodes::InvalidValue);
+        return NvResult::BadValue;
     }
 
     const u32 min_alignment = 0x1000;
@@ -109,12 +120,12 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
     auto object = GetObject(params.handle);
     if (!object) {
         LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
-        return static_cast<u32>(NvErrCodes::InvalidValue);
+        return NvResult::BadValue;
     }
 
     if (object->status == Object::Status::Allocated) {
         LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle);
-        return static_cast<u32>(NvErrCodes::OperationNotPermitted);
+        return NvResult::InsufficientMemory;
     }
 
     object->flags = params.flags;
@@ -124,10 +135,10 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
     object->status = Object::Status::Allocated;
 
     std::memcpy(output.data(), &params, sizeof(params));
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
     IocGetIdParams params;
     std::memcpy(&params, input.data(), sizeof(params));
 
@@ -135,22 +146,22 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
 
     if (!params.handle) {
         LOG_ERROR(Service_NVDRV, "Handle is zero");
-        return static_cast<u32>(NvErrCodes::InvalidValue);
+        return NvResult::BadValue;
     }
 
     auto object = GetObject(params.handle);
     if (!object) {
         LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
-        return static_cast<u32>(NvErrCodes::OperationNotPermitted);
+        return NvResult::BadValue;
     }
 
     params.id = object->id;
 
     std::memcpy(output.data(), &params, sizeof(params));
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
     IocFromIdParams params;
     std::memcpy(&params, input.data(), sizeof(params));
 
@@ -160,13 +171,13 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
                             [&](const auto& entry) { return entry.second->id == params.id; });
     if (itr == handles.end()) {
         LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
-        return static_cast<u32>(NvErrCodes::InvalidValue);
+        return NvResult::BadValue;
     }
 
     auto& object = itr->second;
     if (object->status != Object::Status::Allocated) {
         LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
-        return static_cast<u32>(NvErrCodes::InvalidValue);
+        return NvResult::BadValue;
     }
 
     itr->second->refcount++;
@@ -175,10 +186,10 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
     params.handle = itr->first;
 
     std::memcpy(output.data(), &params, sizeof(params));
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
     enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
 
     IocParamParams params;
@@ -189,12 +200,12 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
     auto object = GetObject(params.handle);
     if (!object) {
         LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
-        return static_cast<u32>(NvErrCodes::InvalidValue);
+        return NvResult::BadValue;
     }
 
     if (object->status != Object::Status::Allocated) {
         LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
-        return static_cast<u32>(NvErrCodes::OperationNotPermitted);
+        return NvResult::BadValue;
     }
 
     switch (static_cast<ParamTypes>(params.param)) {
@@ -216,10 +227,10 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
     }
 
     std::memcpy(output.data(), &params, sizeof(params));
-    return 0;
+    return NvResult::Success;
 }
 
-u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
     // TODO(Subv): These flags are unconfirmed.
     enum FreeFlags {
         Freed = 0,
@@ -234,14 +245,14 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
     auto itr = handles.find(params.handle);
     if (itr == handles.end()) {
         LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
-        return static_cast<u32>(NvErrCodes::InvalidValue);
+        return NvResult::BadValue;
     }
     if (!itr->second->refcount) {
         LOG_ERROR(
             Service_NVDRV,
             "There is no references to this object. The object is already freed. handle={:08X}",
             params.handle);
-        return static_cast<u32>(NvErrCodes::InvalidValue);
+        return NvResult::BadValue;
     }
 
     itr->second->refcount--;
@@ -261,7 +272,7 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
     handles.erase(params.handle);
 
     std::memcpy(output.data(), &params, sizeof(params));
-    return 0;
+    return NvResult::Success;
 }
 
 } // namespace Service::Nvidia::Devices

+ 31 - 37
src/core/hle/service/nvdrv/devices/nvmap.h

@@ -19,13 +19,15 @@ public:
     explicit nvmap(Core::System& system);
     ~nvmap() override;
 
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+    NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                    std::vector<u8>& inline_output) override;
+
     /// Returns the allocated address of an nvmap object given its handle.
     VAddr GetObjectAddress(u32 handle) const;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
-              std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-              IoctlVersion version) override;
-
     /// Represents an nvmap object.
     struct Object {
         enum class Status { Created, Allocated };
@@ -58,76 +60,68 @@ private:
     /// Mapping of currently allocated handles to the objects they represent.
     std::unordered_map<u32, std::shared_ptr<Object>> handles;
 
-    enum class IoctlCommand : u32 {
-        Create = 0xC0080101,
-        FromId = 0xC0080103,
-        Alloc = 0xC0200104,
-        Free = 0xC0180105,
-        Param = 0xC00C0109,
-        GetId = 0xC008010E,
-    };
     struct IocCreateParams {
         // Input
-        u32_le size;
+        u32_le size{};
         // Output
-        u32_le handle;
+        u32_le handle{};
     };
     static_assert(sizeof(IocCreateParams) == 8, "IocCreateParams has wrong size");
 
     struct IocFromIdParams {
         // Input
-        u32_le id;
+        u32_le id{};
         // Output
-        u32_le handle;
+        u32_le handle{};
     };
     static_assert(sizeof(IocFromIdParams) == 8, "IocFromIdParams has wrong size");
 
     struct IocAllocParams {
         // Input
-        u32_le handle;
-        u32_le heap_mask;
-        u32_le flags;
-        u32_le align;
-        u8 kind;
+        u32_le handle{};
+        u32_le heap_mask{};
+        u32_le flags{};
+        u32_le align{};
+        u8 kind{};
         INSERT_PADDING_BYTES(7);
-        u64_le addr;
+        u64_le addr{};
     };
     static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size");
 
     struct IocFreeParams {
-        u32_le handle;
+        u32_le handle{};
         INSERT_PADDING_BYTES(4);
-        u64_le address;
-        u32_le size;
-        u32_le flags;
+        u64_le address{};
+        u32_le size{};
+        u32_le flags{};
     };
     static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size");
 
     struct IocParamParams {
         // Input
-        u32_le handle;
-        u32_le param;
+        u32_le handle{};
+        u32_le param{};
         // Output
-        u32_le result;
+        u32_le result{};
     };
     static_assert(sizeof(IocParamParams) == 12, "IocParamParams has wrong size");
 
     struct IocGetIdParams {
         // Output
-        u32_le id;
+        u32_le id{};
         // Input
-        u32_le handle;
+        u32_le handle{};
     };
     static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
 
     u32 CreateObject(u32 size);
 
-    u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 IocParam(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 IocFree(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output);
+    NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output);
 };
 
 } // namespace Service::Nvidia::Devices

+ 123 - 76
src/core/hle/service/nvdrv/interface.cpp

@@ -23,124 +23,170 @@ void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
 void NVDRV::Open(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_NVDRV, "called");
 
+    if (!is_initialized) {
+        ServiceError(ctx, NvResult::NotInitialized);
+        LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+        return;
+    }
+
     const auto& buffer = ctx.ReadBuffer();
-    std::string device_name(buffer.begin(), buffer.end());
+    const std::string device_name(buffer.begin(), buffer.end());
+    DeviceFD fd = nvdrv->Open(device_name);
 
-    u32 fd = nvdrv->Open(device_name);
     IPC::ResponseBuilder rb{ctx, 4};
     rb.Push(RESULT_SUCCESS);
-    rb.Push<u32>(fd);
-    rb.Push<u32>(0);
+    rb.Push<DeviceFD>(fd);
+    rb.PushEnum(fd != INVALID_NVDRV_FD ? NvResult::Success : NvResult::FileOperationFailed);
+}
+
+void NVDRV::ServiceError(Kernel::HLERequestContext& ctx, NvResult result) {
+    IPC::ResponseBuilder rb{ctx, 3};
+    rb.Push(RESULT_SUCCESS);
+    rb.PushEnum(result);
 }
 
-void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) {
+void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
-    u32 fd = rp.Pop<u32>();
-    u32 command = rp.Pop<u32>();
-
-    /// Ioctl 3 has 2 outputs, first in the input params, second is the result
-    std::vector<u8> output(ctx.GetWriteBufferSize(0));
-    std::vector<u8> output2;
-    if (version == IoctlVersion::Version3) {
-        output2.resize((ctx.GetWriteBufferSize(1)));
+    const auto fd = rp.Pop<DeviceFD>();
+    const auto command = rp.PopRaw<Ioctl>();
+    LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
+
+    if (!is_initialized) {
+        ServiceError(ctx, NvResult::NotInitialized);
+        LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+        return;
     }
 
-    /// Ioctl2 has 2 inputs. It's used to pass data directly instead of providing a pointer.
-    /// KickOfPB uses this
-    auto input = ctx.ReadBuffer(0);
+    // Check device
+    std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
+    const auto input_buffer = ctx.ReadBuffer(0);
 
-    std::vector<u8> input2;
-    if (version == IoctlVersion::Version2) {
-        input2 = ctx.ReadBuffer(1);
-    }
+    const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer);
 
-    IoctlCtrl ctrl{};
-
-    u32 result = nvdrv->Ioctl(fd, command, input, input2, output, output2, ctrl, version);
-
-    if (ctrl.must_delay) {
-        ctrl.fresh_call = false;
-        ctx.SleepClientThread(
-            "NVServices::DelayedResponse", ctrl.timeout,
-            [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
-                      Kernel::ThreadWakeupReason reason) {
-                IoctlCtrl ctrl2{ctrl};
-                std::vector<u8> tmp_output = output;
-                std::vector<u8> tmp_output2 = output2;
-                const u32 ioctl_result = nvdrv->Ioctl(fd, command, input, input2, tmp_output,
-                                                      tmp_output2, ctrl2, version);
-                ctx_.WriteBuffer(tmp_output, 0);
-                if (version == IoctlVersion::Version3) {
-                    ctx_.WriteBuffer(tmp_output2, 1);
-                }
-                IPC::ResponseBuilder rb{ctx_, 3};
-                rb.Push(RESULT_SUCCESS);
-                rb.Push(ioctl_result);
-            },
-            nvdrv->GetEventWriteable(ctrl.event_id));
-    } else {
-        ctx.WriteBuffer(output);
-        if (version == IoctlVersion::Version3) {
-            ctx.WriteBuffer(output2, 1);
-        }
+    if (command.is_out != 0) {
+        ctx.WriteBuffer(output_buffer);
     }
+
     IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(RESULT_SUCCESS);
-    rb.Push(result);
-}
-
-void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
-    LOG_DEBUG(Service_NVDRV, "called");
-    IoctlBase(ctx, IoctlVersion::Version1);
+    rb.PushEnum(nv_result);
 }
 
 void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
-    LOG_DEBUG(Service_NVDRV, "called");
-    IoctlBase(ctx, IoctlVersion::Version2);
+    IPC::RequestParser rp{ctx};
+    const auto fd = rp.Pop<DeviceFD>();
+    const auto command = rp.PopRaw<Ioctl>();
+    LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
+
+    if (!is_initialized) {
+        ServiceError(ctx, NvResult::NotInitialized);
+        LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+        return;
+    }
+
+    const auto input_buffer = ctx.ReadBuffer(0);
+    const auto input_inlined_buffer = ctx.ReadBuffer(1);
+    std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
+
+    const auto nv_result =
+        nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer);
+
+    if (command.is_out != 0) {
+        ctx.WriteBuffer(output_buffer);
+    }
+
+    IPC::ResponseBuilder rb{ctx, 3};
+    rb.Push(RESULT_SUCCESS);
+    rb.PushEnum(nv_result);
 }
 
 void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
-    LOG_DEBUG(Service_NVDRV, "called");
-    IoctlBase(ctx, IoctlVersion::Version3);
+    IPC::RequestParser rp{ctx};
+    const auto fd = rp.Pop<DeviceFD>();
+    const auto command = rp.PopRaw<Ioctl>();
+    LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
+
+    if (!is_initialized) {
+        ServiceError(ctx, NvResult::NotInitialized);
+        LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+        return;
+    }
+
+    const auto input_buffer = ctx.ReadBuffer(0);
+    std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
+    std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1));
+
+    const auto nv_result =
+        nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline);
+
+    if (command.is_out != 0) {
+        ctx.WriteBuffer(output_buffer, 0);
+        ctx.WriteBuffer(output_buffer_inline, 1);
+    }
+
+    IPC::ResponseBuilder rb{ctx, 3};
+    rb.Push(RESULT_SUCCESS);
+    rb.PushEnum(nv_result);
 }
 
 void NVDRV::Close(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_NVDRV, "called");
 
-    IPC::RequestParser rp{ctx};
-    u32 fd = rp.Pop<u32>();
+    if (!is_initialized) {
+        ServiceError(ctx, NvResult::NotInitialized);
+        LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+        return;
+    }
 
-    auto result = nvdrv->Close(fd);
+    IPC::RequestParser rp{ctx};
+    const auto fd = rp.Pop<DeviceFD>();
+    const auto result = nvdrv->Close(fd);
 
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(result);
+    IPC::ResponseBuilder rb{ctx, 3};
+    rb.Push(RESULT_SUCCESS);
+    rb.PushEnum(result);
 }
 
 void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
     LOG_WARNING(Service_NVDRV, "(STUBBED) called");
 
+    is_initialized = true;
+
     IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(RESULT_SUCCESS);
-    rb.Push<u32>(0);
+    rb.PushEnum(NvResult::Success);
 }
 
 void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
-    u32 fd = rp.Pop<u32>();
-    // TODO(Blinkhawk): Figure the meaning of the flag at bit 16
-    u32 event_id = rp.Pop<u32>() & 0x000000FF;
+    const auto fd = rp.Pop<DeviceFD>();
+    const auto event_id = rp.Pop<u32>() & 0x00FF;
     LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id);
 
-    IPC::ResponseBuilder rb{ctx, 3, 1};
-    rb.Push(RESULT_SUCCESS);
+    if (!is_initialized) {
+        ServiceError(ctx, NvResult::NotInitialized);
+        LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+        return;
+    }
+
+    const auto nv_result = nvdrv->VerifyFD(fd);
+    if (nv_result != NvResult::Success) {
+        LOG_ERROR(Service_NVDRV, "Invalid FD specified DeviceFD={}!", fd);
+        ServiceError(ctx, nv_result);
+        return;
+    }
+
     if (event_id < MaxNvEvents) {
+        IPC::ResponseBuilder rb{ctx, 3, 1};
+        rb.Push(RESULT_SUCCESS);
         auto event = nvdrv->GetEvent(event_id);
         event->Clear();
         rb.PushCopyObjects(event);
-        rb.Push<u32>(NvResult::Success);
+        rb.PushEnum(NvResult::Success);
     } else {
-        rb.Push<u32>(0);
-        rb.Push<u32>(NvResult::BadParameter);
+        IPC::ResponseBuilder rb{ctx, 3};
+        rb.Push(RESULT_SUCCESS);
+        rb.PushEnum(NvResult::BadParameter);
     }
 }
 
@@ -151,7 +197,7 @@ void NVDRV::SetAruid(Kernel::HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(RESULT_SUCCESS);
-    rb.Push<u32>(0);
+    rb.PushEnum(NvResult::Success);
 }
 
 void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx) {
@@ -164,8 +210,9 @@ void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ct
 void NVDRV::GetStatus(Kernel::HLERequestContext& ctx) {
     LOG_WARNING(Service_NVDRV, "(STUBBED) called");
 
-    IPC::ResponseBuilder rb{ctx, 2};
+    IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(RESULT_SUCCESS);
+    rb.PushEnum(NvResult::Success);
 }
 
 void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) {
@@ -181,7 +228,7 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
     : ServiceFramework(name), nvdrv(std::move(nvdrv)) {
     static const FunctionInfo functions[] = {
         {0, &NVDRV::Open, "Open"},
-        {1, &NVDRV::Ioctl, "Ioctl"},
+        {1, &NVDRV::Ioctl1, "Ioctl"},
         {2, &NVDRV::Close, "Close"},
         {3, &NVDRV::Initialize, "Initialize"},
         {4, &NVDRV::QueryEvent, "QueryEvent"},

+ 4 - 2
src/core/hle/service/nvdrv/interface.h

@@ -23,7 +23,7 @@ public:
 
 private:
     void Open(Kernel::HLERequestContext& ctx);
-    void Ioctl(Kernel::HLERequestContext& ctx);
+    void Ioctl1(Kernel::HLERequestContext& ctx);
     void Ioctl2(Kernel::HLERequestContext& ctx);
     void Ioctl3(Kernel::HLERequestContext& ctx);
     void Close(Kernel::HLERequestContext& ctx);
@@ -33,11 +33,13 @@ private:
     void SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx);
     void GetStatus(Kernel::HLERequestContext& ctx);
     void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx);
-    void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version);
+
+    void ServiceError(Kernel::HLERequestContext& ctx, NvResult result);
 
     std::shared_ptr<Module> nvdrv;
 
     u64 pid{};
+    bool is_initialized{};
 };
 
 } // namespace Service::Nvidia

+ 66 - 20
src/core/hle/service/nvdrv/nvdata.h

@@ -1,12 +1,16 @@
 #pragma once
 
 #include <array>
+#include "common/bit_field.h"
 #include "common/common_types.h"
 
 namespace Service::Nvidia {
 
 constexpr u32 MaxSyncPoints = 192;
 constexpr u32 MaxNvEvents = 64;
+using DeviceFD = s32;
+
+constexpr DeviceFD INVALID_NVDRV_FD = -1;
 
 struct Fence {
     s32 id;
@@ -20,11 +24,61 @@ struct MultiFence {
     std::array<Fence, 4> fences;
 };
 
-enum NvResult : u32 {
-    Success = 0,
-    BadParameter = 4,
-    Timeout = 5,
-    ResourceError = 15,
+enum class NvResult : u32 {
+    Success = 0x0,
+    NotImplemented = 0x1,
+    NotSupported = 0x2,
+    NotInitialized = 0x3,
+    BadParameter = 0x4,
+    Timeout = 0x5,
+    InsufficientMemory = 0x6,
+    ReadOnlyAttribute = 0x7,
+    InvalidState = 0x8,
+    InvalidAddress = 0x9,
+    InvalidSize = 0xA,
+    BadValue = 0xB,
+    AlreadyAllocated = 0xD,
+    Busy = 0xE,
+    ResourceError = 0xF,
+    CountMismatch = 0x10,
+    OverFlow = 0x11,
+    InsufficientTransferMemory = 0x1000,
+    InsufficientVideoMemory = 0x10000,
+    BadSurfaceColorScheme = 0x10001,
+    InvalidSurface = 0x10002,
+    SurfaceNotSupported = 0x10003,
+    DispInitFailed = 0x20000,
+    DispAlreadyAttached = 0x20001,
+    DispTooManyDisplays = 0x20002,
+    DispNoDisplaysAttached = 0x20003,
+    DispModeNotSupported = 0x20004,
+    DispNotFound = 0x20005,
+    DispAttachDissallowed = 0x20006,
+    DispTypeNotSupported = 0x20007,
+    DispAuthenticationFailed = 0x20008,
+    DispNotAttached = 0x20009,
+    DispSamePwrState = 0x2000A,
+    DispEdidFailure = 0x2000B,
+    DispDsiReadAckError = 0x2000C,
+    DispDsiReadInvalidResp = 0x2000D,
+    FileWriteFailed = 0x30000,
+    FileReadFailed = 0x30001,
+    EndOfFile = 0x30002,
+    FileOperationFailed = 0x30003,
+    DirOperationFailed = 0x30004,
+    EndOfDirList = 0x30005,
+    ConfigVarNotFound = 0x30006,
+    InvalidConfigVar = 0x30007,
+    LibraryNotFound = 0x30008,
+    SymbolNotFound = 0x30009,
+    MemoryMapFailed = 0x3000A,
+    IoctlFailed = 0x3000F,
+    AccessDenied = 0x30010,
+    DeviceNotFound = 0x30011,
+    KernelDriverNotFound = 0x30012,
+    FileNotFound = 0x30013,
+    PathAlreadyExists = 0x30014,
+    ModuleNotPresent = 0xA000E,
 };
 
 enum class EventState {
@@ -34,21 +88,13 @@ enum class EventState {
     Busy = 3,
 };
 
-enum class IoctlVersion : u32 {
-    Version1,
-    Version2,
-    Version3,
-};
-
-struct IoctlCtrl {
-    // First call done to the servioce for services that call itself again after a call.
-    bool fresh_call{true};
-    // Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep
-    bool must_delay{};
-    // Timeout for the delay
-    s64 timeout{};
-    // NV Event Id
-    s32 event_id{-1};
+union Ioctl {
+    u32_le raw;
+    BitField<0, 8, u32> cmd;
+    BitField<8, 8, u32> group;
+    BitField<16, 14, u32> length;
+    BitField<30, 1, u32> is_in;
+    BitField<31, 1, u32> is_out;
 };
 
 } // namespace Service::Nvidia

+ 81 - 16
src/core/hle/service/nvdrv/nvdrv.cpp

@@ -62,36 +62,101 @@ Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} {
 
 Module::~Module() = default;
 
-u32 Module::Open(const std::string& device_name) {
-    ASSERT_MSG(devices.find(device_name) != devices.end(), "Trying to open unknown device {}",
-               device_name);
+NvResult Module::VerifyFD(DeviceFD fd) const {
+    if (fd < 0) {
+        LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
+        return NvResult::InvalidState;
+    }
+
+    if (open_files.find(fd) == open_files.end()) {
+        LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
+        return NvResult::NotImplemented;
+    }
+
+    return NvResult::Success;
+}
+
+DeviceFD Module::Open(const std::string& device_name) {
+    if (devices.find(device_name) == devices.end()) {
+        LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name);
+        return INVALID_NVDRV_FD;
+    }
 
     auto device = devices[device_name];
-    const u32 fd = next_fd++;
+    const DeviceFD fd = next_fd++;
 
     open_files[fd] = std::move(device);
 
     return fd;
 }
 
-u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2,
-                  std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-                  IoctlVersion version) {
-    auto itr = open_files.find(fd);
-    ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
+NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+                        std::vector<u8>& output) {
+    if (fd < 0) {
+        LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
+        return NvResult::InvalidState;
+    }
 
-    auto& device = itr->second;
-    return device->ioctl({command}, input, input2, output, output2, ctrl, version);
+    const auto itr = open_files.find(fd);
+
+    if (itr == open_files.end()) {
+        LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
+        return NvResult::NotImplemented;
+    }
+
+    return itr->second->Ioctl1(command, input, output);
 }
 
-ResultCode Module::Close(u32 fd) {
-    auto itr = open_files.find(fd);
-    ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
+NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+                        const std::vector<u8>& inline_input, std::vector<u8>& output) {
+    if (fd < 0) {
+        LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
+        return NvResult::InvalidState;
+    }
+
+    const auto itr = open_files.find(fd);
+
+    if (itr == open_files.end()) {
+        LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
+        return NvResult::NotImplemented;
+    }
+
+    return itr->second->Ioctl2(command, input, inline_input, output);
+}
+
+NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+                        std::vector<u8>& output, std::vector<u8>& inline_output) {
+    if (fd < 0) {
+        LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
+        return NvResult::InvalidState;
+    }
+
+    const auto itr = open_files.find(fd);
+
+    if (itr == open_files.end()) {
+        LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
+        return NvResult::NotImplemented;
+    }
+
+    return itr->second->Ioctl3(command, input, output, inline_output);
+}
+
+NvResult Module::Close(DeviceFD fd) {
+    if (fd < 0) {
+        LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
+        return NvResult::InvalidState;
+    }
+
+    const auto itr = open_files.find(fd);
+
+    if (itr == open_files.end()) {
+        LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
+        return NvResult::NotImplemented;
+    }
 
     open_files.erase(itr);
 
-    // TODO(flerovium): return correct result code if operation failed.
-    return RESULT_SUCCESS;
+    return NvResult::Success;
 }
 
 void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {

+ 16 - 7
src/core/hle/service/nvdrv/nvdrv.h

@@ -112,14 +112,23 @@ public:
         return std::static_pointer_cast<T>(itr->second);
     }
 
+    NvResult VerifyFD(DeviceFD fd) const;
+
     /// Opens a device node and returns a file descriptor to it.
-    u32 Open(const std::string& device_name);
+    DeviceFD Open(const std::string& device_name);
+
     /// Sends an ioctl command to the specified file descriptor.
-    u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2,
-              std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
-              IoctlVersion version);
+    NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+                    std::vector<u8>& output);
+
+    NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+                    const std::vector<u8>& inline_input, std::vector<u8>& output);
+
+    NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+                    std::vector<u8>& output, std::vector<u8>& inline_output);
+
     /// Closes a device file descriptor and returns operation success.
-    ResultCode Close(u32 fd);
+    NvResult Close(DeviceFD fd);
 
     void SignalSyncpt(const u32 syncpoint_id, const u32 value);
 
@@ -132,10 +141,10 @@ private:
     SyncpointManager syncpoint_manager;
 
     /// Id to use for the next open file descriptor.
-    u32 next_fd = 1;
+    DeviceFD next_fd = 1;
 
     /// Mapping of file descriptors to the devices they reference.
-    std::unordered_map<u32, std::shared_ptr<Devices::nvdevice>> open_files;
+    std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>> open_files;
 
     /// Mapping of device node names to their implementation.
     std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices;