mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	core: Make nvservices more standardized
This commit is contained in:
		
							parent
							
								
									9b24197ca0
								
							
						
					
					
						commit
						31c12de0fe
					
				| @ -24,25 +24,12 @@ 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 ioctl 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 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 Ioctl1(Ioctl command, const std::vector<u8>& input, | ||||
|                             std::vector<u8>& output) = 0; | ||||
|     virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, | ||||
|                             const std::vector<u8>& inline_input, std::vector<u8>& output) = 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; | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -17,59 +17,70 @@ | ||||
| 
 | ||||
| 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); | ||||
|         } | ||||
|         break; | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) { | ||||
|         return Remap(input, output); | ||||
|     } | ||||
| 
 | ||||
|     UNIMPLEMENTED_MSG("Unimplemented ioctl command"); | ||||
|     return 0; | ||||
|     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::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_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); | ||||
|         } | ||||
|     } | ||||
|     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | ||||
|     return NvResult::NotImplemented; | ||||
| } | ||||
| 
 | ||||
| NvResult nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) { | ||||
|     IoctlInitalizeEx params{}; | ||||
|     std::memcpy(¶ms, 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(¶ms, input.data(), input.size()); | ||||
| 
 | ||||
| @ -83,17 +94,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(), ¶ms, 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(¶ms, input.data(), input.size()); | ||||
| 
 | ||||
| @ -104,15 +115,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(), ¶ms, 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 +134,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 +145,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 +154,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(¶ms, input.data(), input.size()); | ||||
| 
 | ||||
| @ -157,7 +168,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(), ¶ms, output.size()); | ||||
|         return NvErrCodes::InvalidInput; | ||||
|         return NvResult::InvalidState; | ||||
|     } | ||||
| 
 | ||||
|     // The real nvservices doesn't make a distinction between handles and ids, and
 | ||||
| @ -184,16 +195,16 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou | ||||
|                              params.mapping_size, params.offset); | ||||
| 
 | ||||
|                 std::memcpy(output.data(), ¶ms, output.size()); | ||||
|                 return NvErrCodes::InvalidInput; | ||||
|                 return NvResult::InvalidState; | ||||
|             } | ||||
| 
 | ||||
|             std::memcpy(output.data(), ¶ms, output.size()); | ||||
|             return NvErrCodes::Success; | ||||
|             return NvResult::Success; | ||||
|         } else { | ||||
|             LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset); | ||||
| 
 | ||||
|             std::memcpy(output.data(), ¶ms, output.size()); | ||||
|             return NvErrCodes::InvalidInput; | ||||
|             return NvResult::InvalidState; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -213,10 +224,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 +236,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(¶ms, input.data(), input.size()); | ||||
| 
 | ||||
| @ -238,20 +249,19 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou | ||||
|     } | ||||
| 
 | ||||
|     std::memcpy(output.data(), ¶ms, 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(¶ms, 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; | ||||
| } | ||||
| 
 | ||||
| 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) { | ||||
|     IoctlGetVaRegions params{}; | ||||
|     std::memcpy(¶ms, input.data(), input.size()); | ||||
| 
 | ||||
| @ -270,7 +280,31 @@ 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(), ¶ms, output.size()); | ||||
|     return 0; | ||||
|     return NvResult::Success; | ||||
| } | ||||
| 
 | ||||
| NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output, | ||||
|                                      std::vector<u8>& inline_output) { | ||||
|     IoctlGetVaRegions params{}; | ||||
|     std::memcpy(¶ms, 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(), ¶ms, output.size()); | ||||
|     std::memcpy(inline_output.data(), ¶ms.regions, inline_output.size()); | ||||
|     return NvResult::Success; | ||||
| } | ||||
| 
 | ||||
| std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const { | ||||
|  | ||||
| @ -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{}; | ||||
| 
 | ||||
|     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 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); | ||||
| 
 | ||||
|     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); | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
| 
 | ||||
| u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { | ||||
| 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; | ||||
| } | ||||
| 
 | ||||
| NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { | ||||
|     IocGetConfigParams params{}; | ||||
|     std::memcpy(¶ms, 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(¶ms, 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(), ¶ms, 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(¶ms, 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(¶ms, 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(¶ms, input.data(), sizeof(params)); | ||||
| 
 | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -15,39 +15,112 @@ 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); | ||||
|     default: | ||||
|         UNIMPLEMENTED_MSG("Unimplemented ioctl"); | ||||
|         return 0; | ||||
| 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; | ||||
| } | ||||
| 
 | ||||
| u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, | ||||
|                                         std::vector<u8>& output2, IoctlVersion version) { | ||||
| 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: | ||||
|         break; | ||||
|     } | ||||
|     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | ||||
|     return NvResult::NotImplemented; | ||||
| } | ||||
| 
 | ||||
| NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, | ||||
|                                              std::vector<u8>& output) { | ||||
|     LOG_DEBUG(Service_NVDRV, "called"); | ||||
|     IoctlCharacteristics params{}; | ||||
|     std::memcpy(¶ms, 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(), ¶ms, output.size()); | ||||
|     return NvResult::Success; | ||||
| } | ||||
| 
 | ||||
| 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(¶ms, input.data(), input.size()); | ||||
| @ -89,35 +162,36 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto | ||||
|     params.gpu_characteristics_buf_size = 0xA0; | ||||
|     params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
 | ||||
| 
 | ||||
|     if (version == IoctlVersion::Version3) { | ||||
|         std::memcpy(output.data(), input.data(), output.size()); | ||||
|         std::memcpy(output2.data(), ¶ms.gc, output2.size()); | ||||
|     } else { | ||||
|         std::memcpy(output.data(), ¶ms, output.size()); | ||||
|     } | ||||
|     return 0; | ||||
|     std::memcpy(output.data(), input.data(), output.size()); | ||||
|     std::memcpy(inline_output.data(), ¶ms.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(¶ms, 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; | ||||
|     } | ||||
| 
 | ||||
|     if (version == IoctlVersion::Version3) { | ||||
|         std::memcpy(output.data(), input.data(), output.size()); | ||||
|         std::memcpy(output2.data(), ¶ms.tcp_mask, output2.size()); | ||||
|     } else { | ||||
|         std::memcpy(output.data(), ¶ms, output.size()); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
|     std::memcpy(output.data(), ¶ms, output.size()); | ||||
|     return NvResult::Success; | ||||
| } | ||||
| 
 | ||||
| u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) { | ||||
| NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, | ||||
|                                       std::vector<u8>& inline_output) { | ||||
|     IoctlGpuGetTpcMasksArgs params{}; | ||||
|     std::memcpy(¶ms, 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(), ¶ms, output.size()); | ||||
|     std::memcpy(inline_output.data(), ¶ms.tcp_mask, inline_output.size()); | ||||
|     return NvResult::Success; | ||||
| } | ||||
| 
 | ||||
| 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(), ¶ms, 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(), ¶ms, 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(), ¶ms, 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(¶ms, input.data(), input.size()); | ||||
|     // TODO(ogniK): What does this even actually do?
 | ||||
|     std::memcpy(output.data(), ¶ms, 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(¶ms, input.data(), input.size()); | ||||
|     // TODO : To implement properly
 | ||||
|     std::memcpy(output.data(), ¶ms, 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(¶ms, input.data(), input.size()); | ||||
|     // TODO : To implement properly
 | ||||
|     std::memcpy(output.data(), ¶ms, 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(¶ms, input.data(), input.size()); | ||||
|     params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count()); | ||||
|     std::memcpy(output.data(), ¶ms, output.size()); | ||||
|     return 0; | ||||
|     return NvResult::Success; | ||||
| } | ||||
| 
 | ||||
| } // namespace Service::Nvidia::Devices
 | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
| 
 | ||||
|     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); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     UNIMPLEMENTED_MSG("Unimplemented ioctl"); | ||||
|     return 0; | ||||
|     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::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; | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
| 
 | ||||
| NvResult nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { | ||||
|     IoctlSetNvmapFD params{}; | ||||
|     std::memcpy(¶ms, 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(¶ms, 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(¶ms, input.data(), input.size()); | ||||
|     params.data = user_data; | ||||
|     std::memcpy(output.data(), ¶ms, 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(¶ms, 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(), ¶ms, 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(¶ms, 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(), ¶ms, 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(¶ms, 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(), ¶ms, 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(), ¶ms, 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(¶ms, 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)); | ||||
| 
 | ||||
|     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) { | ||||
|     if (input.size() < sizeof(IoctlSubmitGpfifo)) { | ||||
|         UNIMPLEMENTED(); | ||||
|     } | ||||
|     IoctlSubmitGpfifo params{}; | ||||
|     std::memcpy(¶ms, 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 { | ||||
|     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::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { | ||||
| 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(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); | ||||
|     Tegra::CommandList entries(params.num_entries); | ||||
|     std::memcpy(entries.command_lists.data(), input_inline.data(), input_inline.size()); | ||||
|     return SubmitGPFIFOImpl(params, output, std::move(entries)); | ||||
| } | ||||
| 
 | ||||
| NvResult nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { | ||||
|     IoctlGetWaitbase params{}; | ||||
|     std::memcpy(¶ms, 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(), ¶ms, 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(¶ms, 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(¶ms, 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
 | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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(¶ms, 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(¶ms, 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(¶ms, 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(), ¶ms, 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(¶ms, input.data(), sizeof(IoctlGetWaitbase)); | ||||
|     params.value = 0; // Seems to be hard coded at 0
 | ||||
|     std::memcpy(output.data(), ¶ms, 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(¶ms, 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(), ¶ms, 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(¶ms, 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(), ¶ms, 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( | ||||
|  | ||||
| @ -18,9 +18,12 @@ 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; | ||||
|     virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, | ||||
|                             std::vector<u8>& output) = 0; | ||||
|     virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, | ||||
|                             const std::vector<u8>& inline_input, std::vector<u8>& output) = 0; | ||||
|     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 +66,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; | ||||
| 
 | ||||
|  | ||||
| @ -13,28 +13,43 @@ 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()); | ||||
| NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input, | ||||
|                               std::vector<u8>& output) { | ||||
| 
 | ||||
|     switch (static_cast<IoctlCommand>(command.raw)) { | ||||
|     case IoctlCommand::IocSetNVMAPfdCommand: | ||||
|         return SetNVMAPfd(input, output); | ||||
|     switch (command.group) { | ||||
|     case 'H': | ||||
|         switch (command.cmd) { | ||||
|         case 0x1: | ||||
|             return SetNVMAPfd(input, output); | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|         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(¶ms, 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
 | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -15,36 +15,43 @@ 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); | ||||
|         } | ||||
|     case 'H': | ||||
|         switch (command.cmd) { | ||||
|         case 0x1: | ||||
|             return SetNVMAPfd(input); | ||||
|         } | ||||
|         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
 | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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,41 @@ 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); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     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 +61,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 +76,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(¶ms, 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(), ¶ms, 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(¶ms, 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 +115,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 +130,10 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { | ||||
|     object->status = Object::Status::Allocated; | ||||
| 
 | ||||
|     std::memcpy(output.data(), ¶ms, 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(¶ms, input.data(), sizeof(params)); | ||||
| 
 | ||||
| @ -135,22 +141,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(), ¶ms, 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(¶ms, input.data(), sizeof(params)); | ||||
| 
 | ||||
| @ -160,13 +166,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 +181,10 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) { | ||||
|     params.handle = itr->first; | ||||
| 
 | ||||
|     std::memcpy(output.data(), ¶ms, 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 +195,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 +222,10 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) { | ||||
|     } | ||||
| 
 | ||||
|     std::memcpy(output.data(), ¶ms, 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 +240,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 +267,7 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { | ||||
|     handles.erase(params.handle); | ||||
| 
 | ||||
|     std::memcpy(output.data(), ¶ms, sizeof(params)); | ||||
|     return 0; | ||||
|     return NvResult::Success; | ||||
| } | ||||
| 
 | ||||
| } // namespace Service::Nvidia::Devices
 | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -23,124 +23,171 @@ void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { | ||||
| void NVDRV::Open(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_DEBUG(Service_NVDRV, "called"); | ||||
| 
 | ||||
|     const auto& buffer = ctx.ReadBuffer(); | ||||
|     std::string device_name(buffer.begin(), buffer.end()); | ||||
|     if (!initialized) { | ||||
|         ServiceError(ctx, NvResult::NotInitialized); | ||||
|         LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     const auto& buffer = ctx.ReadBuffer(); | ||||
|     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::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) { | ||||
|     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))); | ||||
|     } | ||||
| 
 | ||||
|     /// 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, 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); | ||||
|         } | ||||
|     } | ||||
| void NVDRV::ServiceError(Kernel::HLERequestContext& ctx, NvResult result) { | ||||
|     IPC::ResponseBuilder rb{ctx, 3}; | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push(result); | ||||
|     rb.PushEnum(result); | ||||
| } | ||||
| 
 | ||||
| void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_DEBUG(Service_NVDRV, "called"); | ||||
|     IoctlBase(ctx, IoctlVersion::Version1); | ||||
| void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) { | ||||
|     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 (!initialized) { | ||||
|         ServiceError(ctx, NvResult::NotInitialized); | ||||
|         LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // Check device
 | ||||
|     std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); | ||||
|     const auto input_buffer = ctx.ReadBuffer(0); | ||||
| 
 | ||||
|     const auto nv_result = nvdrv->Ioctl1(fd, command, input_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::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 (!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 (!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"); | ||||
| 
 | ||||
|     if (!initialized) { | ||||
|         ServiceError(ctx, NvResult::NotInitialized); | ||||
|         LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     u32 fd = rp.Pop<u32>(); | ||||
|     const auto fd = rp.Pop<DeviceFD>(); | ||||
|     const auto result = nvdrv->Close(fd); | ||||
| 
 | ||||
|     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"); | ||||
| 
 | ||||
|     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 (!initialized) { | ||||
|         ServiceError(ctx, NvResult::NotInitialized); | ||||
|         LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     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 +198,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 +211,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 +229,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"}, | ||||
|  | ||||
| @ -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 initialized{}; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Service::Nvidia
 | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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); | ||||
|     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) { | ||||
| 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; | ||||
|     } | ||||
| 
 | ||||
|     auto itr = open_files.find(fd); | ||||
|     ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     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) { | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Chloe Marcec
						Chloe Marcec