nvmap.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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);
  33. case 0x5:
  34. return WrapFixed(this, &nvmap::IocFree, input, output);
  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(DeviceFD fd) {}
  60. void nvmap::OnClose(DeviceFD fd) {}
  61. NvResult nvmap::IocCreate(IocCreateParams& params) {
  62. LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size);
  63. std::shared_ptr<NvCore::NvMap::Handle> handle_description{};
  64. auto result =
  65. file.CreateHandle(Common::AlignUp(params.size, YUZU_PAGESIZE), handle_description);
  66. if (result != NvResult::Success) {
  67. LOG_CRITICAL(Service_NVDRV, "Failed to create Object");
  68. return result;
  69. }
  70. handle_description->orig_size = params.size; // Orig size is the unaligned size
  71. params.handle = handle_description->id;
  72. LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size);
  73. return NvResult::Success;
  74. }
  75. NvResult nvmap::IocAlloc(IocAllocParams& params) {
  76. LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address);
  77. if (!params.handle) {
  78. LOG_CRITICAL(Service_NVDRV, "Handle is 0");
  79. return NvResult::BadValue;
  80. }
  81. if ((params.align - 1) & params.align) {
  82. LOG_CRITICAL(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align);
  83. return NvResult::BadValue;
  84. }
  85. // Force page size alignment at a minimum
  86. if (params.align < YUZU_PAGESIZE) {
  87. params.align = YUZU_PAGESIZE;
  88. }
  89. auto handle_description{file.GetHandle(params.handle)};
  90. if (!handle_description) {
  91. LOG_CRITICAL(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
  92. return NvResult::BadValue;
  93. }
  94. if (handle_description->allocated) {
  95. LOG_CRITICAL(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle);
  96. return NvResult::InsufficientMemory;
  97. }
  98. const auto result =
  99. handle_description->Alloc(params.flags, params.align, params.kind, params.address);
  100. if (result != NvResult::Success) {
  101. LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle);
  102. return result;
  103. }
  104. bool is_out_io{};
  105. ASSERT(system.ApplicationProcess()
  106. ->GetPageTable()
  107. .LockForMapDeviceAddressSpace(&is_out_io, handle_description->address,
  108. handle_description->size,
  109. Kernel::KMemoryPermission::None, true, false)
  110. .IsSuccess());
  111. return result;
  112. }
  113. NvResult nvmap::IocGetId(IocGetIdParams& params) {
  114. LOG_DEBUG(Service_NVDRV, "called");
  115. // See the comment in FromId for extra info on this function
  116. if (!params.handle) {
  117. LOG_CRITICAL(Service_NVDRV, "Error!");
  118. return NvResult::BadValue;
  119. }
  120. auto handle_description{file.GetHandle(params.handle)};
  121. if (!handle_description) {
  122. LOG_CRITICAL(Service_NVDRV, "Error!");
  123. return NvResult::AccessDenied; // This will always return EPERM irrespective of if the
  124. // handle exists or not
  125. }
  126. params.id = handle_description->id;
  127. return NvResult::Success;
  128. }
  129. NvResult nvmap::IocFromId(IocFromIdParams& params) {
  130. LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id);
  131. // Handles and IDs are always the same value in nvmap however IDs can be used globally given the
  132. // right permissions.
  133. // Since we don't plan on ever supporting multiprocess we can skip implementing handle refs and
  134. // so this function just does simple validation and passes through the handle id.
  135. if (!params.id) {
  136. LOG_CRITICAL(Service_NVDRV, "Zero Id is invalid!");
  137. return NvResult::BadValue;
  138. }
  139. auto handle_description{file.GetHandle(params.id)};
  140. if (!handle_description) {
  141. LOG_CRITICAL(Service_NVDRV, "Unregistered handle!");
  142. return NvResult::BadValue;
  143. }
  144. auto result = handle_description->Duplicate(false);
  145. if (result != NvResult::Success) {
  146. LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!");
  147. return result;
  148. }
  149. params.handle = handle_description->id;
  150. return NvResult::Success;
  151. }
  152. NvResult nvmap::IocParam(IocParamParams& params) {
  153. enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
  154. LOG_DEBUG(Service_NVDRV, "called type={}", params.param);
  155. if (!params.handle) {
  156. LOG_CRITICAL(Service_NVDRV, "Invalid handle!");
  157. return NvResult::BadValue;
  158. }
  159. auto handle_description{file.GetHandle(params.handle)};
  160. if (!handle_description) {
  161. LOG_CRITICAL(Service_NVDRV, "Not registered handle!");
  162. return NvResult::BadValue;
  163. }
  164. switch (params.param) {
  165. case HandleParameterType::Size:
  166. params.result = static_cast<u32_le>(handle_description->orig_size);
  167. break;
  168. case HandleParameterType::Alignment:
  169. params.result = static_cast<u32_le>(handle_description->align);
  170. break;
  171. case HandleParameterType::Base:
  172. params.result = static_cast<u32_le>(-22); // posix EINVAL
  173. break;
  174. case HandleParameterType::Heap:
  175. if (handle_description->allocated)
  176. params.result = 0x40000000;
  177. else
  178. params.result = 0;
  179. break;
  180. case HandleParameterType::Kind:
  181. params.result = handle_description->kind;
  182. break;
  183. case HandleParameterType::IsSharedMemMapped:
  184. params.result = handle_description->is_shared_mem_mapped;
  185. break;
  186. default:
  187. return NvResult::BadValue;
  188. }
  189. return NvResult::Success;
  190. }
  191. NvResult nvmap::IocFree(IocFreeParams& params) {
  192. LOG_DEBUG(Service_NVDRV, "called");
  193. if (!params.handle) {
  194. LOG_CRITICAL(Service_NVDRV, "Handle null freed?");
  195. return NvResult::Success;
  196. }
  197. if (auto freeInfo{file.FreeHandle(params.handle, false)}) {
  198. if (freeInfo->can_unlock) {
  199. ASSERT(system.ApplicationProcess()
  200. ->GetPageTable()
  201. .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size)
  202. .IsSuccess());
  203. }
  204. params.address = freeInfo->address;
  205. params.size = static_cast<u32>(freeInfo->size);
  206. params.flags.raw = 0;
  207. params.flags.map_uncached.Assign(freeInfo->was_uncached);
  208. } else {
  209. // This is possible when there's internal dups or other duplicates.
  210. }
  211. return NvResult::Success;
  212. }
  213. } // namespace Service::Nvidia::Devices