nvmap.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <algorithm>
  4. #include <bit>
  5. #include <cstring>
  6. #include "common/alignment.h"
  7. #include "common/assert.h"
  8. #include "common/logging/log.h"
  9. #include "core/core.h"
  10. #include "core/hle/kernel/k_page_table.h"
  11. #include "core/hle/kernel/k_process.h"
  12. #include "core/hle/service/nvdrv/core/container.h"
  13. #include "core/hle/service/nvdrv/core/nvmap.h"
  14. #include "core/hle/service/nvdrv/devices/nvmap.h"
  15. #include "core/memory.h"
  16. using Core::Memory::YUZU_PAGESIZE;
  17. namespace Service::Nvidia::Devices {
  18. nvmap::nvmap(Core::System& system_, NvCore::Container& container_)
  19. : nvdevice{system_}, container{container_}, file{container.GetNvMapFile()} {}
  20. nvmap::~nvmap() = default;
  21. NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
  22. std::vector<u8>& output) {
  23. switch (command.group) {
  24. case 0x1:
  25. switch (command.cmd) {
  26. case 0x1:
  27. return IocCreate(input, output);
  28. case 0x3:
  29. return IocFromId(input, output);
  30. case 0x4:
  31. return IocAlloc(input, output);
  32. case 0x5:
  33. return IocFree(input, output);
  34. case 0x9:
  35. return IocParam(input, output);
  36. case 0xe:
  37. return IocGetId(input, output);
  38. default:
  39. break;
  40. }
  41. break;
  42. default:
  43. break;
  44. }
  45. UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
  46. return NvResult::NotImplemented;
  47. }
  48. NvResult nvmap::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
  49. std::span<const u8> inline_input, std::vector<u8>& output) {
  50. UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
  51. return NvResult::NotImplemented;
  52. }
  53. NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
  54. std::vector<u8>& output, std::vector<u8>& inline_output) {
  55. UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
  56. return NvResult::NotImplemented;
  57. }
  58. void nvmap::OnOpen(DeviceFD fd) {}
  59. void nvmap::OnClose(DeviceFD fd) {}
  60. NvResult nvmap::IocCreate(std::span<const u8> input, std::vector<u8>& output) {
  61. IocCreateParams params;
  62. std::memcpy(&params, input.data(), sizeof(params));
  63. LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size);
  64. std::shared_ptr<NvCore::NvMap::Handle> handle_description{};
  65. auto result =
  66. file.CreateHandle(Common::AlignUp(params.size, YUZU_PAGESIZE), handle_description);
  67. if (result != NvResult::Success) {
  68. LOG_CRITICAL(Service_NVDRV, "Failed to create Object");
  69. return result;
  70. }
  71. handle_description->orig_size = params.size; // Orig size is the unaligned size
  72. params.handle = handle_description->id;
  73. LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size);
  74. std::memcpy(output.data(), &params, sizeof(params));
  75. return NvResult::Success;
  76. }
  77. NvResult nvmap::IocAlloc(std::span<const u8> input, std::vector<u8>& output) {
  78. IocAllocParams params;
  79. std::memcpy(&params, input.data(), sizeof(params));
  80. LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address);
  81. if (!params.handle) {
  82. LOG_CRITICAL(Service_NVDRV, "Handle is 0");
  83. return NvResult::BadValue;
  84. }
  85. if ((params.align - 1) & params.align) {
  86. LOG_CRITICAL(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align);
  87. return NvResult::BadValue;
  88. }
  89. // Force page size alignment at a minimum
  90. if (params.align < YUZU_PAGESIZE) {
  91. params.align = YUZU_PAGESIZE;
  92. }
  93. auto handle_description{file.GetHandle(params.handle)};
  94. if (!handle_description) {
  95. LOG_CRITICAL(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
  96. return NvResult::BadValue;
  97. }
  98. if (handle_description->allocated) {
  99. LOG_CRITICAL(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle);
  100. return NvResult::InsufficientMemory;
  101. }
  102. const auto result =
  103. handle_description->Alloc(params.flags, params.align, params.kind, params.address);
  104. if (result != NvResult::Success) {
  105. LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle);
  106. return result;
  107. }
  108. bool is_out_io{};
  109. ASSERT(system.CurrentProcess()
  110. ->PageTable()
  111. .LockForMapDeviceAddressSpace(&is_out_io, handle_description->address,
  112. handle_description->size,
  113. Kernel::KMemoryPermission::None, true, false)
  114. .IsSuccess());
  115. std::memcpy(output.data(), &params, sizeof(params));
  116. return result;
  117. }
  118. NvResult nvmap::IocGetId(std::span<const u8> input, std::vector<u8>& output) {
  119. IocGetIdParams params;
  120. std::memcpy(&params, input.data(), sizeof(params));
  121. LOG_DEBUG(Service_NVDRV, "called");
  122. // See the comment in FromId for extra info on this function
  123. if (!params.handle) {
  124. LOG_CRITICAL(Service_NVDRV, "Error!");
  125. return NvResult::BadValue;
  126. }
  127. auto handle_description{file.GetHandle(params.handle)};
  128. if (!handle_description) {
  129. LOG_CRITICAL(Service_NVDRV, "Error!");
  130. return NvResult::AccessDenied; // This will always return EPERM irrespective of if the
  131. // handle exists or not
  132. }
  133. params.id = handle_description->id;
  134. std::memcpy(output.data(), &params, sizeof(params));
  135. return NvResult::Success;
  136. }
  137. NvResult nvmap::IocFromId(std::span<const u8> input, std::vector<u8>& output) {
  138. IocFromIdParams params;
  139. std::memcpy(&params, input.data(), sizeof(params));
  140. LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id);
  141. // Handles and IDs are always the same value in nvmap however IDs can be used globally given the
  142. // right permissions.
  143. // Since we don't plan on ever supporting multiprocess we can skip implementing handle refs and
  144. // so this function just does simple validation and passes through the handle id.
  145. if (!params.id) {
  146. LOG_CRITICAL(Service_NVDRV, "Zero Id is invalid!");
  147. return NvResult::BadValue;
  148. }
  149. auto handle_description{file.GetHandle(params.id)};
  150. if (!handle_description) {
  151. LOG_CRITICAL(Service_NVDRV, "Unregistered handle!");
  152. return NvResult::BadValue;
  153. }
  154. auto result = handle_description->Duplicate(false);
  155. if (result != NvResult::Success) {
  156. LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!");
  157. return result;
  158. }
  159. params.handle = handle_description->id;
  160. std::memcpy(output.data(), &params, sizeof(params));
  161. return NvResult::Success;
  162. }
  163. NvResult nvmap::IocParam(std::span<const u8> input, std::vector<u8>& output) {
  164. enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
  165. IocParamParams params;
  166. std::memcpy(&params, input.data(), sizeof(params));
  167. LOG_DEBUG(Service_NVDRV, "called type={}", params.param);
  168. if (!params.handle) {
  169. LOG_CRITICAL(Service_NVDRV, "Invalid handle!");
  170. return NvResult::BadValue;
  171. }
  172. auto handle_description{file.GetHandle(params.handle)};
  173. if (!handle_description) {
  174. LOG_CRITICAL(Service_NVDRV, "Not registered handle!");
  175. return NvResult::BadValue;
  176. }
  177. switch (params.param) {
  178. case HandleParameterType::Size:
  179. params.result = static_cast<u32_le>(handle_description->orig_size);
  180. break;
  181. case HandleParameterType::Alignment:
  182. params.result = static_cast<u32_le>(handle_description->align);
  183. break;
  184. case HandleParameterType::Base:
  185. params.result = static_cast<u32_le>(-22); // posix EINVAL
  186. break;
  187. case HandleParameterType::Heap:
  188. if (handle_description->allocated)
  189. params.result = 0x40000000;
  190. else
  191. params.result = 0;
  192. break;
  193. case HandleParameterType::Kind:
  194. params.result = handle_description->kind;
  195. break;
  196. case HandleParameterType::IsSharedMemMapped:
  197. params.result = handle_description->is_shared_mem_mapped;
  198. break;
  199. default:
  200. return NvResult::BadValue;
  201. }
  202. std::memcpy(output.data(), &params, sizeof(params));
  203. return NvResult::Success;
  204. }
  205. NvResult nvmap::IocFree(std::span<const u8> input, std::vector<u8>& output) {
  206. IocFreeParams params;
  207. std::memcpy(&params, input.data(), sizeof(params));
  208. LOG_DEBUG(Service_NVDRV, "called");
  209. if (!params.handle) {
  210. LOG_CRITICAL(Service_NVDRV, "Handle null freed?");
  211. return NvResult::Success;
  212. }
  213. if (auto freeInfo{file.FreeHandle(params.handle, false)}) {
  214. if (freeInfo->can_unlock) {
  215. ASSERT(system.CurrentProcess()
  216. ->PageTable()
  217. .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size)
  218. .IsSuccess());
  219. }
  220. params.address = freeInfo->address;
  221. params.size = static_cast<u32>(freeInfo->size);
  222. params.flags.raw = 0;
  223. params.flags.map_uncached.Assign(freeInfo->was_uncached);
  224. } else {
  225. // This is possible when there's internel dups or other duplicates.
  226. }
  227. std::memcpy(output.data(), &params, sizeof(params));
  228. return NvResult::Success;
  229. }
  230. } // namespace Service::Nvidia::Devices