diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 5b8248433e..1b52511a5a 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -9,6 +9,7 @@
 #include "common/common_types.h"
 #include "common/swap.h"
 #include "core/hle/service/nvdrv/nvdata.h"
+#include "core/hle/service/service.h"
 
 namespace Core {
 class System;
@@ -38,8 +39,9 @@ public:
      * @param output A buffer where the output data will be written to.
      * @returns The result code of the ioctl.
      */
-    virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                      IoctlCtrl& ctrl) = 0;
+    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;
 
 protected:
     Core::System& system;
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 926a1285d7..f764388bcf 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -17,8 +17,9 @@ 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, std::vector<u8>& output,
-                        IoctlCtrl& ctrl) {
+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;
 }
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index e79e490ff0..6fcdeee845 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -20,8 +20,9 @@ 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, std::vector<u8>& output,
-              IoctlCtrl& 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;
 
     /// 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,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 24ab3f2e9f..6bc053f273 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -26,8 +26,9 @@ nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_
     : 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, std::vector<u8>& output,
-                         IoctlCtrl& ctrl) {
+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());
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index 30ca5f4c3b..169fb8f0e8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -20,8 +20,9 @@ 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, std::vector<u8>& output,
-              IoctlCtrl& 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;
 
 private:
     enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 9a66a5f880..ff6b1abaeb 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -19,8 +19,9 @@ nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface)
     : nvdevice(system), events_interface{events_interface} {}
 nvhost_ctrl::~nvhost_ctrl() = default;
 
-u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                       IoctlCtrl& ctrl) {
+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());
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 14e6e7e57a..9898623dea 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -17,8 +17,9 @@ public:
     explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface);
     ~nvhost_ctrl() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-              IoctlCtrl& 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;
 
 private:
     enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 988effd90b..389ace76fb 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -15,14 +15,15 @@ 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, std::vector<u8>& output,
-                           IoctlCtrl& ctrl) {
+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);
+        return GetCharacteristics(input, output, output2, version);
     case IoctlCommand::IocGetTPCMasksCommand:
         return GetTPCMasks(input, output);
     case IoctlCommand::IocGetActiveSlotMaskCommand:
@@ -44,7 +45,8 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec
     return 0;
 }
 
-u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
+                                        std::vector<u8>& output2, IoctlVersion version) {
     LOG_DEBUG(Service_NVDRV, "called");
     IoctlCharacteristics params{};
     std::memcpy(&params, input.data(), input.size());
@@ -85,7 +87,13 @@ 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());
+
+    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;
 }
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index 2b035ae3f9..642b0a2cb1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -16,8 +16,9 @@ public:
     explicit nvhost_ctrl_gpu(Core::System& system);
     ~nvhost_ctrl_gpu() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-              IoctlCtrl& 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;
 
 private:
     enum class IoctlCommand : u32_le {
@@ -162,7 +163,8 @@ private:
     };
     static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size");
 
-    u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
+    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);
     u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
     u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 241dac8817..29c4c0abb5 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -17,8 +17,9 @@ nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
     : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
 nvhost_gpu::~nvhost_gpu() = default;
 
-u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                      IoctlCtrl& ctrl) {
+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());
 
@@ -50,7 +51,7 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u
             return SubmitGPFIFO(input, output);
         }
         if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) {
-            return KickoffPB(input, output);
+            return KickoffPB(input, output, input2, version);
         }
     }
 
@@ -173,7 +174,8 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
     return 0;
 }
 
-u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
+                          const std::vector<u8>& input2, IoctlVersion version) {
     if (input.size() < sizeof(IoctlSubmitGpfifo)) {
         UNIMPLEMENTED();
     }
@@ -183,9 +185,13 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
                 params.address, params.num_entries, params.flags.raw);
 
     Tegra::CommandList entries(params.num_entries);
-    Memory::ReadBlock(params.address, entries.data(),
-                      params.num_entries * sizeof(Tegra::CommandListHeader));
-
+    if (version == IoctlVersion::Version2) {
+        std::memcpy(entries.data(), input2.data(),
+                    params.num_entries * sizeof(Tegra::CommandListHeader));
+    } else {
+        Memory::ReadBlock(params.address, entries.data(),
+                          params.num_entries * sizeof(Tegra::CommandListHeader));
+    }
     UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
     UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index d2e8fbae9a..d056dd046e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -24,8 +24,9 @@ public:
     explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
     ~nvhost_gpu() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-              IoctlCtrl& 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;
 
 private:
     enum class IoctlCommand : u32_le {
@@ -183,7 +184,8 @@ private:
     u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
     u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
     u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
-    u32 KickoffPB(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);
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index f572ad30f3..bdae8b8875 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -13,8 +13,9 @@ namespace Service::Nvidia::Devices {
 nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {}
 nvhost_nvdec::~nvhost_nvdec() = default;
 
-u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                        IoctlCtrl& ctrl) {
+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());
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 2710f05113..cbdac8069d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -16,8 +16,9 @@ public:
     explicit nvhost_nvdec(Core::System& system);
     ~nvhost_nvdec() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-              IoctlCtrl& 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;
 
 private:
     enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index 38282956fb..96e7b7dabe 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -13,8 +13,9 @@ 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, std::vector<u8>& output,
-                        IoctlCtrl& ctrl) {
+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());
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 3797666936..98dcac52fd 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -16,8 +16,9 @@ public:
     explicit nvhost_nvjpg(Core::System& system);
     ~nvhost_nvjpg() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-              IoctlCtrl& 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;
 
 private:
     enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 70e8091dbc..c695b88632 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -13,8 +13,9 @@ namespace Service::Nvidia::Devices {
 nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {}
 nvhost_vic::~nvhost_vic() = default;
 
-u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                      IoctlCtrl& ctrl) {
+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());
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index 7d111977e3..bec32bea15 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -16,8 +16,9 @@ public:
     explicit nvhost_vic(Core::System& system);
     ~nvhost_vic() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-              IoctlCtrl& 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;
 
 private:
     enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 223b496b74..8c742316ca 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -28,8 +28,9 @@ VAddr nvmap::GetObjectAddress(u32 handle) const {
     return object->addr;
 }
 
-u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                 IoctlCtrl& ctrl) {
+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);
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index bf4a101c22..73c2e88094 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -22,8 +22,9 @@ public:
     /// 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, std::vector<u8>& output,
-              IoctlCtrl& 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;
 
     /// Represents an nvmap object.
     struct Object {
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index d5be64ed28..b154d598ae 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -33,18 +33,30 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) {
     rb.Push<u32>(0);
 }
 
-void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
-    LOG_DEBUG(Service_NVDRV, "called");
-
+void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) {
     IPC::RequestParser rp{ctx};
     u32 fd = rp.Pop<u32>();
     u32 command = rp.Pop<u32>();
 
-    std::vector<u8> output(ctx.GetWriteBufferSize());
+    /// 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)));
+    }
+
+    /// Ioctl2 has 2 inputs. It's used to pass data directly instead of providing a pointer.
+    /// KickOfPB uses this
+    auto input = ctx.ReadBuffer(0);
+
+    std::vector<u8> input2;
+    if (version == IoctlVersion::Version2) {
+        input2 = ctx.ReadBuffer(1);
+    }
 
     IoctlCtrl ctrl{};
 
-    u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output, ctrl);
+    u32 result = nvdrv->Ioctl(fd, command, input, input2, output, output2, ctrl, version);
 
     if (ctrl.must_delay) {
         ctrl.fresh_call = false;
@@ -53,9 +65,14 @@ void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
             [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
                 Kernel::ThreadWakeupReason reason) {
                 IoctlCtrl ctrl2{ctrl};
-                std::vector<u8> output2 = output;
-                u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output2, ctrl2);
-                ctx.WriteBuffer(output2);
+                std::vector<u8> tmp_output = output;
+                std::vector<u8> tmp_output2 = output2;
+                u32 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(result);
@@ -63,12 +80,30 @@ void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
             nvdrv->GetEventWriteable(ctrl.event_id));
     } else {
         ctx.WriteBuffer(output);
+        if (version == IoctlVersion::Version3) {
+            ctx.WriteBuffer(output2, 1);
+        }
     }
     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);
+}
+
+void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
+    LOG_DEBUG(Service_NVDRV, "called");
+    IoctlBase(ctx, IoctlVersion::Version2);
+}
+
+void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
+    LOG_DEBUG(Service_NVDRV, "called");
+    IoctlBase(ctx, IoctlVersion::Version3);
+}
+
 void NVDRV::Close(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_NVDRV, "called");
 
@@ -154,8 +189,8 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
         {8, &NVDRV::SetClientPID, "SetClientPID"},
         {9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"},
         {10, nullptr, "InitializeDevtools"},
-        {11, &NVDRV::Ioctl, "Ioctl2"},
-        {12, nullptr, "Ioctl3"},
+        {11, &NVDRV::Ioctl2, "Ioctl2"},
+        {12, &NVDRV::Ioctl3, "Ioctl3"},
         {13, &NVDRV::FinishInitialize, "FinishInitialize"},
     };
     RegisterHandlers(functions);
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index 10a0ecd52d..9269ce00cf 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -24,6 +24,8 @@ public:
 private:
     void Open(Kernel::HLERequestContext& ctx);
     void Ioctl(Kernel::HLERequestContext& ctx);
+    void Ioctl2(Kernel::HLERequestContext& ctx);
+    void Ioctl3(Kernel::HLERequestContext& ctx);
     void Close(Kernel::HLERequestContext& ctx);
     void Initialize(Kernel::HLERequestContext& ctx);
     void QueryEvent(Kernel::HLERequestContext& ctx);
@@ -31,6 +33,7 @@ private:
     void FinishInitialize(Kernel::HLERequestContext& ctx);
     void GetStatus(Kernel::HLERequestContext& ctx);
     void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx);
+    void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version);
 
     std::shared_ptr<Module> nvdrv;
 
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index ac03cbc232..529b034712 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -34,6 +34,12 @@ 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};
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 2011a226af..307a7e9281 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -71,13 +71,14 @@ u32 Module::Open(const std::string& device_name) {
     return fd;
 }
 
-u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output,
-                  IoctlCtrl& ctrl) {
+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");
 
     auto& device = itr->second;
-    return device->ioctl({command}, input, output, ctrl);
+    return device->ioctl({command}, input, input2, output, output2, ctrl, version);
 }
 
 ResultCode Module::Close(u32 fd) {
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index a339ab672b..f8bb289693 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -106,8 +106,9 @@ public:
     /// Opens a device node and returns a file descriptor to it.
     u32 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, std::vector<u8>& output,
-              IoctlCtrl& ctrl);
+    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);
     /// Closes a device file descriptor and returns operation success.
     ResultCode Close(u32 fd);