nvmap.cpp 8.8 KB

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