From 94a5e97eb3e331f5c02e3ff2c0d4a9d62f072ba7 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 7 Jan 2018 21:25:57 -0500 Subject: [PATCH] NV: Implemented the nvdrv:a service and the /dev/nvmap device. --- src/core/hle/service/nvdrv/nvdrv.cpp | 16 ++ src/core/hle/service/nvdrv/nvdrv.h | 25 +++ src/core/hle/service/nvdrv/nvdrv_a.cpp | 283 +++++++++++++++++++++++++ src/core/hle/service/nvdrv/nvdrv_a.h | 30 +++ 4 files changed, 354 insertions(+) create mode 100644 src/core/hle/service/nvdrv/nvdrv.cpp create mode 100644 src/core/hle/service/nvdrv/nvdrv.h create mode 100644 src/core/hle/service/nvdrv/nvdrv_a.cpp create mode 100644 src/core/hle/service/nvdrv/nvdrv_a.h 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 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvdrv/nvdrv_a.h" + +namespace Service { +namespace NVDRV { + +void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared()->InstallAsService(service_manager); +} + +} // namespace nvdrv +} // 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 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include "common/common_types.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace NVDRV { + +class nvdevice { +public: + virtual ~nvdevice() = default; + + virtual u32 ioctl(u32 command, const std::vector& input, std::vector& output) = 0; +}; + +/// Registers all NVDRV services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace NVDRV +} // 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 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvdrv/nvdrv_a.h" + +namespace Service { +namespace NVDRV { + +class nvhost_as_gpu : public nvdevice { +public: + u32 ioctl(u32 command, const std::vector& input, std::vector& output) override { + ASSERT(false, "Unimplemented"); + return 0; + } +}; + +class nvmap : public nvdevice { +public: + u32 ioctl(u32 command, const std::vector& input, std::vector& output) override { + switch (command) { + case IocCreateCommand: + return IocCreate(input, output); + case IocAllocCommand: + return IocAlloc(input, output); + case IocGetIdCommand: + return IocGetId(input, output); + case IocFromIdCommand: + return IocFromId(input, output); + case IocParamCommand: + return IocParam(input, output); + } + + ASSERT(false, "Unimplemented"); + } + +private: + // Represents an nvmap object. + struct Object { + enum class Status { Created, Allocated }; + u32 id; + u32 size; + u32 flags; + u32 align; + u8 kind; + u64 addr; + Status status; + }; + + u32 next_handle = 1; + u32 next_id = 1; + std::unordered_map> handles; + + enum IoctlCommands { + IocCreateCommand = 0xC0080101, + IocFromIdCommand = 0xC0080103, + IocAllocCommand = 0xC0200104, + IocParamCommand = 0xC00C0109, + IocGetIdCommand = 0xC008010E + }; + + struct IocCreateParams { + // Input + u32_le size; + // Output + u32_le handle; + }; + + struct IocAllocParams { + // Input + u32_le handle; + u32_le heap_mask; + u32_le flags; + u32_le align; + u8 kind; + INSERT_PADDING_BYTES(7); + u64_le addr; + }; + + struct IocGetIdParams { + // Output + u32_le id; + // Input + u32_le handle; + }; + + struct IocFromIdParams { + // Input + u32_le id; + // Output + u32_le handle; + }; + + struct IocParamParams { + // Input + u32_le handle; + u32_le type; + // Output + u32_le value; + }; + + u32 IocCreate(const std::vector& input, std::vector& output) { + IocCreateParams params; + std::memcpy(¶ms, input.data(), sizeof(params)); + + // Create a new nvmap object and obtain a handle to it. + auto object = std::make_shared(); + object->id = next_id++; + object->size = params.size; + object->status = Object::Status::Created; + + u32 handle = next_handle++; + handles[handle] = std::move(object); + + LOG_WARNING(Service, "(STUBBED) size 0x%08X", params.size); + + params.handle = handle; + + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; + } + + u32 IocAlloc(const std::vector& input, std::vector& output) { + IocAllocParams params; + std::memcpy(¶ms, input.data(), sizeof(params)); + + auto itr = handles.find(params.handle); + ASSERT(itr != handles.end()); + + auto object = itr->second; + object->flags = params.flags; + object->align = params.align; + object->kind = params.kind; + object->addr = params.addr; + object->status = Object::Status::Allocated; + + LOG_WARNING(Service, "(STUBBED) Allocated address 0x%llx", params.addr); + + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; + } + + u32 IocGetId(const std::vector& input, std::vector& output) { + IocGetIdParams params; + std::memcpy(¶ms, input.data(), sizeof(params)); + + LOG_WARNING(Service, "called"); + + auto itr = handles.find(params.handle); + ASSERT(itr != handles.end()); + + params.id = itr->second->id; + + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; + } + + u32 IocFromId(const std::vector& input, std::vector& output) { + IocFromIdParams params; + std::memcpy(¶ms, input.data(), sizeof(params)); + + LOG_WARNING(Service, "(STUBBED) called"); + + auto itr = std::find_if(handles.begin(), handles.end(), + [&](const auto& entry) { return entry.second->id == params.id; }); + ASSERT(itr != handles.end()); + + // Make a new handle for the object + u32 handle = next_handle++; + handles[handle] = itr->second; + + params.handle = handle; + + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; + } + + u32 IocParam(const std::vector& input, std::vector& output) { + enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 }; + + IocParamParams params; + std::memcpy(¶ms, input.data(), sizeof(params)); + + LOG_WARNING(Service, "(STUBBED) called type=%u", params.type); + + auto itr = handles.find(params.handle); + ASSERT(itr != handles.end()); + + auto object = itr->second; + ASSERT(object->status == Object::Status::Allocated); + + switch (static_cast(params.type)) { + case ParamTypes::Size: + params.value = object->size; + break; + case ParamTypes::Alignment: + params.value = object->align; + break; + case ParamTypes::Heap: + // TODO(Subv): Seems to be a hardcoded value? + params.value = 0x40000000; + break; + case ParamTypes::Kind: + params.value = object->kind; + break; + default: + ASSERT(false, "Unimplemented"); + } + + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; + } +}; + +void NVDRV_A::Open(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + auto buffer = ctx.BufferDescriptorA()[0]; + + std::string device_name = Memory::ReadCString(buffer.Address(), buffer.Size()); + + auto device = devices[device_name]; + u32 fd = next_fd++; + + open_files[fd] = device; + + IPC::RequestBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(fd); + rb.Push(0); +} + +void NVDRV_A::Ioctl(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::RequestParser rp{ctx}; + u32 fd = rp.Pop(); + u32 command = rp.Pop(); + + auto input_buffer = ctx.BufferDescriptorA()[0]; + auto output_buffer = ctx.BufferDescriptorB()[0]; + + std::vector input(input_buffer.Size()); + std::vector output(output_buffer.Size()); + + Memory::ReadBlock(input_buffer.Address(), input.data(), input_buffer.Size()); + auto itr = open_files.find(fd); + ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); + + auto device = itr->second; + u32 nv_result = device->ioctl(command, input, output); + + Memory::WriteBlock(output_buffer.Address(), output.data(), output_buffer.Size()); + + IPC::RequestBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(nv_result); +} + +void NVDRV_A::Initialize(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + IPC::RequestBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(0); +} + +NVDRV_A::NVDRV_A() : ServiceFramework("nvdrv:a") { + static const FunctionInfo functions[] = { + {0, &NVDRV_A::Open, "Open"}, + {1, &NVDRV_A::Ioctl, "Ioctl"}, + {3, &NVDRV_A::Initialize, "Initialize"}, + }; + RegisterHandlers(functions); + + devices["/dev/nvhost-as-gpu"] = std::make_shared(); + devices["/dev/nvmap"] = std::make_shared(); +} + +} // namespace NVDRV +} // 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 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" +#include + +namespace Service { +namespace NVDRV { + +class NVDRV_A final : public ServiceFramework { +public: + NVDRV_A(); + ~NVDRV_A() = default; + +private: + void Open(Kernel::HLERequestContext& ctx); + void Ioctl(Kernel::HLERequestContext& ctx); + void Initialize(Kernel::HLERequestContext& ctx); + + u32 next_fd = 1; + + std::unordered_map> open_files; + std::unordered_map> devices; +}; + +} // namespace NVDRV +} // namespace Service