diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 8de8705966..126782573a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -42,6 +42,9 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u
         if (command.cmd == NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO) {
             return SubmitGPFIFO(input, output);
         }
+        if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) {
+            return KickoffPB(input, output);
+        }
     }
 
     UNIMPLEMENTED_MSG("Unimplemented ioctl");
@@ -127,14 +130,37 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
     IoctlSubmitGpfifo params{};
     std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
     LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
-                params.gpfifo, params.num_entries, params.flags);
+                params.address, params.num_entries, params.flags);
 
     auto entries = std::vector<IoctlGpfifoEntry>();
     entries.resize(params.num_entries);
     std::memcpy(&entries[0], &input.data()[sizeof(IoctlSubmitGpfifo)],
                 params.num_entries * sizeof(IoctlGpfifoEntry));
     for (auto entry : entries) {
-        VAddr va_addr = entry.Address();
+        Tegra::GPUVAddr va_addr = entry.Address();
+        Core::System::GetInstance().GPU().ProcessCommandList(va_addr, entry.sz);
+    }
+    params.fence_out.id = 0;
+    params.fence_out.value = 0;
+    std::memcpy(output.data(), &params, output.size());
+    return 0;
+}
+
+u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output) {
+    if (input.size() < sizeof(IoctlSubmitGpfifo)) {
+        UNIMPLEMENTED();
+    }
+    IoctlSubmitGpfifo params{};
+    std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
+    LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
+                params.address, params.num_entries, params.flags);
+
+    std::vector<IoctlGpfifoEntry> entries(params.num_entries);
+    Memory::ReadBlock(params.address, entries.data(),
+                      params.num_entries * sizeof(IoctlGpfifoEntry));
+
+    for (auto entry : entries) {
+        Tegra::GPUVAddr va_addr = entry.Address();
         Core::System::GetInstance().GPU().ProcessCommandList(va_addr, entry.sz);
     }
     params.fence_out.id = 0;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index c9f6b9b6aa..aa8df2e6eb 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -15,6 +15,7 @@ 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:
@@ -158,14 +159,14 @@ private:
             BitField<31, 1, u32_le> unk2;
         };
 
-        VAddr Address() const {
-            return (static_cast<VAddr>(gpu_va_hi) << 32) | entry0;
+        Tegra::GPUVAddr Address() const {
+            return (static_cast<Tegra::GPUVAddr>(gpu_va_hi) << 32) | entry0;
         }
     };
     static_assert(sizeof(IoctlGpfifoEntry) == 8, "IoctlGpfifoEntry is incorrect size");
 
     struct IoctlSubmitGpfifo {
-        u64_le gpfifo;      // (ignored) pointer to gpfifo fence structs
+        u64_le address;     // pointer to gpfifo entry structs
         u32_le num_entries; // number of fence objects being submitted
         u32_le flags;
         IoctlFence fence_out; // returned new fence object for others to wait on
@@ -193,6 +194,7 @@ 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 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/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index b10efd5c94..1b497b8145 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -101,7 +101,7 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
         {8, &NVDRV::SetClientPID, "SetClientPID"},
         {9, nullptr, "DumpGraphicsMemoryInfo"},
         {10, nullptr, "InitializeDevtools"},
-        {11, nullptr, "Ioctl2"},
+        {11, &NVDRV::Ioctl, "Ioctl2"},
         {12, nullptr, "Ioctl3"},
         {13, &NVDRV::FinishInitialize, "FinishInitialize"},
     };