diff options
| author | 2018-01-07 21:25:57 -0500 | |
|---|---|---|
| committer | 2018-01-10 23:28:05 -0500 | |
| commit | 94a5e97eb3e331f5c02e3ff2c0d4a9d62f072ba7 (patch) | |
| tree | 3067e1a6e631253b880851e739a47926d75fe15a /src | |
| parent | IPC: Corrected some definitions for the buffer C descriptor flags. (diff) | |
| download | yuzu-94a5e97eb3e331f5c02e3ff2c0d4a9d62f072ba7.tar.gz yuzu-94a5e97eb3e331f5c02e3ff2c0d4a9d62f072ba7.tar.xz yuzu-94a5e97eb3e331f5c02e3ff2c0d4a9d62f072ba7.zip | |
NV: Implemented the nvdrv:a service and the /dev/nvmap device.
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/service/nvdrv/nvdrv.cpp | 16 | ||||
| -rw-r--r-- | src/core/hle/service/nvdrv/nvdrv.h | 25 | ||||
| -rw-r--r-- | src/core/hle/service/nvdrv/nvdrv_a.cpp | 283 | ||||
| -rw-r--r-- | src/core/hle/service/nvdrv/nvdrv_a.h | 30 |
4 files changed, 354 insertions, 0 deletions
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp new file mode 100644 index 000000000..a2d55eaee --- /dev/null +++ b/src/core/hle/service/nvdrv/nvdrv.cpp | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/hle/service/nvdrv/nvdrv.h" | ||
| 6 | #include "core/hle/service/nvdrv/nvdrv_a.h" | ||
| 7 | |||
| 8 | namespace Service { | ||
| 9 | namespace NVDRV { | ||
| 10 | |||
| 11 | void InstallInterfaces(SM::ServiceManager& service_manager) { | ||
| 12 | std::make_shared<NVDRV_A>()->InstallAsService(service_manager); | ||
| 13 | } | ||
| 14 | |||
| 15 | } // namespace nvdrv | ||
| 16 | } // namespace Service | ||
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h new file mode 100644 index 000000000..a8f305d33 --- /dev/null +++ b/src/core/hle/service/nvdrv/nvdrv.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <vector> | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "core/hle/service/service.h" | ||
| 10 | |||
| 11 | namespace Service { | ||
| 12 | namespace NVDRV { | ||
| 13 | |||
| 14 | class nvdevice { | ||
| 15 | public: | ||
| 16 | virtual ~nvdevice() = default; | ||
| 17 | |||
| 18 | virtual u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) = 0; | ||
| 19 | }; | ||
| 20 | |||
| 21 | /// Registers all NVDRV services with the specified service manager. | ||
| 22 | void InstallInterfaces(SM::ServiceManager& service_manager); | ||
| 23 | |||
| 24 | } // namespace NVDRV | ||
| 25 | } // namespace Service | ||
diff --git a/src/core/hle/service/nvdrv/nvdrv_a.cpp b/src/core/hle/service/nvdrv/nvdrv_a.cpp new file mode 100644 index 000000000..af6b7f7aa --- /dev/null +++ b/src/core/hle/service/nvdrv/nvdrv_a.cpp | |||
| @@ -0,0 +1,283 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/hle/ipc_helpers.h" | ||
| 7 | #include "core/hle/service/nvdrv/nvdrv.h" | ||
| 8 | #include "core/hle/service/nvdrv/nvdrv_a.h" | ||
| 9 | |||
| 10 | namespace Service { | ||
| 11 | namespace NVDRV { | ||
| 12 | |||
| 13 | class nvhost_as_gpu : public nvdevice { | ||
| 14 | public: | ||
| 15 | u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) override { | ||
| 16 | ASSERT(false, "Unimplemented"); | ||
| 17 | return 0; | ||
| 18 | } | ||
| 19 | }; | ||
| 20 | |||
| 21 | class nvmap : public nvdevice { | ||
| 22 | public: | ||
| 23 | u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) override { | ||
| 24 | switch (command) { | ||
| 25 | case IocCreateCommand: | ||
| 26 | return IocCreate(input, output); | ||
| 27 | case IocAllocCommand: | ||
| 28 | return IocAlloc(input, output); | ||
| 29 | case IocGetIdCommand: | ||
| 30 | return IocGetId(input, output); | ||
| 31 | case IocFromIdCommand: | ||
| 32 | return IocFromId(input, output); | ||
| 33 | case IocParamCommand: | ||
| 34 | return IocParam(input, output); | ||
| 35 | } | ||
| 36 | |||
| 37 | ASSERT(false, "Unimplemented"); | ||
| 38 | } | ||
| 39 | |||
| 40 | private: | ||
| 41 | // Represents an nvmap object. | ||
| 42 | struct Object { | ||
| 43 | enum class Status { Created, Allocated }; | ||
| 44 | u32 id; | ||
| 45 | u32 size; | ||
| 46 | u32 flags; | ||
| 47 | u32 align; | ||
| 48 | u8 kind; | ||
| 49 | u64 addr; | ||
| 50 | Status status; | ||
| 51 | }; | ||
| 52 | |||
| 53 | u32 next_handle = 1; | ||
| 54 | u32 next_id = 1; | ||
| 55 | std::unordered_map<u32, std::shared_ptr<Object>> handles; | ||
| 56 | |||
| 57 | enum IoctlCommands { | ||
| 58 | IocCreateCommand = 0xC0080101, | ||
| 59 | IocFromIdCommand = 0xC0080103, | ||
| 60 | IocAllocCommand = 0xC0200104, | ||
| 61 | IocParamCommand = 0xC00C0109, | ||
| 62 | IocGetIdCommand = 0xC008010E | ||
| 63 | }; | ||
| 64 | |||
| 65 | struct IocCreateParams { | ||
| 66 | // Input | ||
| 67 | u32_le size; | ||
| 68 | // Output | ||
| 69 | u32_le handle; | ||
| 70 | }; | ||
| 71 | |||
| 72 | struct IocAllocParams { | ||
| 73 | // Input | ||
| 74 | u32_le handle; | ||
| 75 | u32_le heap_mask; | ||
| 76 | u32_le flags; | ||
| 77 | u32_le align; | ||
| 78 | u8 kind; | ||
| 79 | INSERT_PADDING_BYTES(7); | ||
| 80 | u64_le addr; | ||
| 81 | }; | ||
| 82 | |||
| 83 | struct IocGetIdParams { | ||
| 84 | // Output | ||
| 85 | u32_le id; | ||
| 86 | // Input | ||
| 87 | u32_le handle; | ||
| 88 | }; | ||
| 89 | |||
| 90 | struct IocFromIdParams { | ||
| 91 | // Input | ||
| 92 | u32_le id; | ||
| 93 | // Output | ||
| 94 | u32_le handle; | ||
| 95 | }; | ||
| 96 | |||
| 97 | struct IocParamParams { | ||
| 98 | // Input | ||
| 99 | u32_le handle; | ||
| 100 | u32_le type; | ||
| 101 | // Output | ||
| 102 | u32_le value; | ||
| 103 | }; | ||
| 104 | |||
| 105 | u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 106 | IocCreateParams params; | ||
| 107 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 108 | |||
| 109 | // Create a new nvmap object and obtain a handle to it. | ||
| 110 | auto object = std::make_shared<Object>(); | ||
| 111 | object->id = next_id++; | ||
| 112 | object->size = params.size; | ||
| 113 | object->status = Object::Status::Created; | ||
| 114 | |||
| 115 | u32 handle = next_handle++; | ||
| 116 | handles[handle] = std::move(object); | ||
| 117 | |||
| 118 | LOG_WARNING(Service, "(STUBBED) size 0x%08X", params.size); | ||
| 119 | |||
| 120 | params.handle = handle; | ||
| 121 | |||
| 122 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 123 | return 0; | ||
| 124 | } | ||
| 125 | |||
| 126 | u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 127 | IocAllocParams params; | ||
| 128 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 129 | |||
| 130 | auto itr = handles.find(params.handle); | ||
| 131 | ASSERT(itr != handles.end()); | ||
| 132 | |||
| 133 | auto object = itr->second; | ||
| 134 | object->flags = params.flags; | ||
| 135 | object->align = params.align; | ||
| 136 | object->kind = params.kind; | ||
| 137 | object->addr = params.addr; | ||
| 138 | object->status = Object::Status::Allocated; | ||
| 139 | |||
| 140 | LOG_WARNING(Service, "(STUBBED) Allocated address 0x%llx", params.addr); | ||
| 141 | |||
| 142 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 143 | return 0; | ||
| 144 | } | ||
| 145 | |||
| 146 | u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 147 | IocGetIdParams params; | ||
| 148 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 149 | |||
| 150 | LOG_WARNING(Service, "called"); | ||
| 151 | |||
| 152 | auto itr = handles.find(params.handle); | ||
| 153 | ASSERT(itr != handles.end()); | ||
| 154 | |||
| 155 | params.id = itr->second->id; | ||
| 156 | |||
| 157 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | u32 IocFromId(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 162 | IocFromIdParams params; | ||
| 163 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 164 | |||
| 165 | LOG_WARNING(Service, "(STUBBED) called"); | ||
| 166 | |||
| 167 | auto itr = std::find_if(handles.begin(), handles.end(), | ||
| 168 | [&](const auto& entry) { return entry.second->id == params.id; }); | ||
| 169 | ASSERT(itr != handles.end()); | ||
| 170 | |||
| 171 | // Make a new handle for the object | ||
| 172 | u32 handle = next_handle++; | ||
| 173 | handles[handle] = itr->second; | ||
| 174 | |||
| 175 | params.handle = handle; | ||
| 176 | |||
| 177 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 178 | return 0; | ||
| 179 | } | ||
| 180 | |||
| 181 | u32 IocParam(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 182 | enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 }; | ||
| 183 | |||
| 184 | IocParamParams params; | ||
| 185 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 186 | |||
| 187 | LOG_WARNING(Service, "(STUBBED) called type=%u", params.type); | ||
| 188 | |||
| 189 | auto itr = handles.find(params.handle); | ||
| 190 | ASSERT(itr != handles.end()); | ||
| 191 | |||
| 192 | auto object = itr->second; | ||
| 193 | ASSERT(object->status == Object::Status::Allocated); | ||
| 194 | |||
| 195 | switch (static_cast<ParamTypes>(params.type)) { | ||
| 196 | case ParamTypes::Size: | ||
| 197 | params.value = object->size; | ||
| 198 | break; | ||
| 199 | case ParamTypes::Alignment: | ||
| 200 | params.value = object->align; | ||
| 201 | break; | ||
| 202 | case ParamTypes::Heap: | ||
| 203 | // TODO(Subv): Seems to be a hardcoded value? | ||
| 204 | params.value = 0x40000000; | ||
| 205 | break; | ||
| 206 | case ParamTypes::Kind: | ||
| 207 | params.value = object->kind; | ||
| 208 | break; | ||
| 209 | default: | ||
| 210 | ASSERT(false, "Unimplemented"); | ||
| 211 | } | ||
| 212 | |||
| 213 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 214 | return 0; | ||
| 215 | } | ||
| 216 | }; | ||
| 217 | |||
| 218 | void NVDRV_A::Open(Kernel::HLERequestContext& ctx) { | ||
| 219 | LOG_WARNING(Service, "(STUBBED) called"); | ||
| 220 | |||
| 221 | auto buffer = ctx.BufferDescriptorA()[0]; | ||
| 222 | |||
| 223 | std::string device_name = Memory::ReadCString(buffer.Address(), buffer.Size()); | ||
| 224 | |||
| 225 | auto device = devices[device_name]; | ||
| 226 | u32 fd = next_fd++; | ||
| 227 | |||
| 228 | open_files[fd] = device; | ||
| 229 | |||
| 230 | IPC::RequestBuilder rb{ctx, 4}; | ||
| 231 | rb.Push(RESULT_SUCCESS); | ||
| 232 | rb.Push<u32>(fd); | ||
| 233 | rb.Push<u32>(0); | ||
| 234 | } | ||
| 235 | |||
| 236 | void NVDRV_A::Ioctl(Kernel::HLERequestContext& ctx) { | ||
| 237 | LOG_WARNING(Service, "(STUBBED) called"); | ||
| 238 | |||
| 239 | IPC::RequestParser rp{ctx}; | ||
| 240 | u32 fd = rp.Pop<u32>(); | ||
| 241 | u32 command = rp.Pop<u32>(); | ||
| 242 | |||
| 243 | auto input_buffer = ctx.BufferDescriptorA()[0]; | ||
| 244 | auto output_buffer = ctx.BufferDescriptorB()[0]; | ||
| 245 | |||
| 246 | std::vector<u8> input(input_buffer.Size()); | ||
| 247 | std::vector<u8> output(output_buffer.Size()); | ||
| 248 | |||
| 249 | Memory::ReadBlock(input_buffer.Address(), input.data(), input_buffer.Size()); | ||
| 250 | auto itr = open_files.find(fd); | ||
| 251 | ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); | ||
| 252 | |||
| 253 | auto device = itr->second; | ||
| 254 | u32 nv_result = device->ioctl(command, input, output); | ||
| 255 | |||
| 256 | Memory::WriteBlock(output_buffer.Address(), output.data(), output_buffer.Size()); | ||
| 257 | |||
| 258 | IPC::RequestBuilder rb{ctx, 3}; | ||
| 259 | rb.Push(RESULT_SUCCESS); | ||
| 260 | rb.Push(nv_result); | ||
| 261 | } | ||
| 262 | |||
| 263 | void NVDRV_A::Initialize(Kernel::HLERequestContext& ctx) { | ||
| 264 | LOG_WARNING(Service, "(STUBBED) called"); | ||
| 265 | IPC::RequestBuilder rb{ctx, 3}; | ||
| 266 | rb.Push(RESULT_SUCCESS); | ||
| 267 | rb.Push<u32>(0); | ||
| 268 | } | ||
| 269 | |||
| 270 | NVDRV_A::NVDRV_A() : ServiceFramework("nvdrv:a") { | ||
| 271 | static const FunctionInfo functions[] = { | ||
| 272 | {0, &NVDRV_A::Open, "Open"}, | ||
| 273 | {1, &NVDRV_A::Ioctl, "Ioctl"}, | ||
| 274 | {3, &NVDRV_A::Initialize, "Initialize"}, | ||
| 275 | }; | ||
| 276 | RegisterHandlers(functions); | ||
| 277 | |||
| 278 | devices["/dev/nvhost-as-gpu"] = std::make_shared<nvhost_as_gpu>(); | ||
| 279 | devices["/dev/nvmap"] = std::make_shared<nvmap>(); | ||
| 280 | } | ||
| 281 | |||
| 282 | } // namespace NVDRV | ||
| 283 | } // namespace Service | ||
diff --git a/src/core/hle/service/nvdrv/nvdrv_a.h b/src/core/hle/service/nvdrv/nvdrv_a.h new file mode 100644 index 000000000..09522a486 --- /dev/null +++ b/src/core/hle/service/nvdrv/nvdrv_a.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | #include <memory> | ||
| 9 | |||
| 10 | namespace Service { | ||
| 11 | namespace NVDRV { | ||
| 12 | |||
| 13 | class NVDRV_A final : public ServiceFramework<NVDRV_A> { | ||
| 14 | public: | ||
| 15 | NVDRV_A(); | ||
| 16 | ~NVDRV_A() = default; | ||
| 17 | |||
| 18 | private: | ||
| 19 | void Open(Kernel::HLERequestContext& ctx); | ||
| 20 | void Ioctl(Kernel::HLERequestContext& ctx); | ||
| 21 | void Initialize(Kernel::HLERequestContext& ctx); | ||
| 22 | |||
| 23 | u32 next_fd = 1; | ||
| 24 | |||
| 25 | std::unordered_map<u32, std::shared_ptr<nvdevice>> open_files; | ||
| 26 | std::unordered_map<std::string, std::shared_ptr<nvdevice>> devices; | ||
| 27 | }; | ||
| 28 | |||
| 29 | } // namespace NVDRV | ||
| 30 | } // namespace Service | ||