From a19bf973b1355af5fb9fa12e3bc797c7d09dd8ec Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Sat, 17 Jul 2021 17:13:15 +0100 Subject: [PATCH] Rework the nvdrv core for accuracy and support new deserialisation Devices will need to be moved over in order to support this new interface, IOCTL2/3 support will be added in a later commit. --- .../skyline/services/nvdrv/INvDrvServices.cpp | 136 ++++++------------ .../skyline/services/nvdrv/INvDrvServices.h | 17 ++- .../cpp/skyline/services/nvdrv/core/core.h | 19 +++ .../services/nvdrv/devices/nvdevice.cpp | 35 +---- .../skyline/services/nvdrv/devices/nvdevice.h | 92 ++---------- .../cpp/skyline/services/nvdrv/driver.cpp | 130 ++++++++++++----- .../main/cpp/skyline/services/nvdrv/driver.h | 65 +++------ .../main/cpp/skyline/services/nvdrv/types.h | 93 ++++++++++++ .../main/cpp/skyline/services/serviceman.cpp | 13 +- 9 files changed, 300 insertions(+), 300 deletions(-) create mode 100644 app/src/main/cpp/skyline/services/nvdrv/core/core.h create mode 100644 app/src/main/cpp/skyline/services/nvdrv/types.h diff --git a/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.cpp b/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.cpp index 6fbd10bd..ba40777a 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.cpp +++ b/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.cpp @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MPL-2.0 +// SPDX-License-Identifier: MIT OR MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #include @@ -6,72 +6,72 @@ #include "driver.h" #include "devices/nvdevice.h" +#define NVRESULT(x) [&response](NvResult err) { response.Push(err); return Result{}; }(x) + namespace skyline::service::nvdrv { - INvDrvServices::INvDrvServices(const DeviceState &state, ServiceManager &manager) : driver(nvdrv::driver.expired() ? std::make_shared(state) : nvdrv::driver.lock()), BaseService(state, manager) { - if (nvdrv::driver.expired()) - nvdrv::driver = driver; - } + INvDrvServices::INvDrvServices(const DeviceState &state, ServiceManager &manager, Driver &driver, const SessionPermissions &perms) : BaseService(state, manager), driver(driver), ctx(SessionContext{.perms = perms}) {} Result INvDrvServices::Open(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - auto path{request.inputBuf.at(0).as_string()}; + constexpr FileDescriptor SessionFdLimit{sizeof(u64) * 2 * 8}; //!< Nvdrv uses two 64 bit variables to store a bitset - response.Push(driver->OpenDevice(path)); - response.Push(device::NvStatus::Success); + auto path{request.inputBuf.at(0).as_string(true)}; + if (path.empty() || nextFdIndex == SessionFdLimit) { + response.Push(InvalidFileDescriptor); + return NVRESULT(NvResult::FileOperationFailed); + } - return {}; + if (auto err{driver.OpenDevice(path, nextFdIndex, ctx)}; err != NvResult::Success) { + response.Push(InvalidFileDescriptor); + return NVRESULT(err); + } + + response.Push(nextFdIndex++); + return NVRESULT(NvResult::Success); } Result INvDrvServices::Ioctl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - auto fd{request.Pop()}; - auto cmd{request.Pop()}; + auto fd{request.Pop()}; + auto ioctl{request.Pop()}; - auto device{driver->GetDevice(fd)}; + auto inBuf{request.inputBuf.at(0)}; + auto outBuf{request.outputBuf.at(0)}; - // Strip the permissions from the command leaving only the ID - cmd &= 0xFFFF; + if (ioctl.in && inBuf.size() < ioctl.size) + return NVRESULT(NvResult::InvalidSize); - span buffer{}; - if (request.inputBuf.empty() || request.outputBuf.empty()) { - if (!request.inputBuf.empty()) - buffer = request.inputBuf.at(0); - else if (!request.outputBuf.empty()) - buffer = request.outputBuf.at(0); - else - throw exception("No IOCTL Buffers"); - } else if (request.inputBuf[0].data() == request.outputBuf[0].data()) { - if (request.inputBuf[0].size() >= request.outputBuf[0].size()) - buffer = request.inputBuf[0]; - else - buffer = request.outputBuf[0]; - } else { - throw exception("IOCTL Input Buffer (0x{:X}) != Output Buffer (0x{:X})", request.inputBuf[0].data(), request.outputBuf[0].data()); + if (ioctl.out && outBuf.size() < ioctl.size) + return NVRESULT(NvResult::InvalidSize); + + if (ioctl.in && ioctl.out) { + if (outBuf.size() < inBuf.size()) + return NVRESULT(NvResult::InvalidSize); + + // Copy in buf to out buf for inout ioctls to avoid needing to pass around two buffers everywhere + if (outBuf.data() != inBuf.data()) + outBuf.copy_from(inBuf, ioctl.size); } - response.Push(device->HandleIoctl(cmd, device::IoctlType::Ioctl, buffer, {})); - return {}; + return NVRESULT(driver.Ioctl(fd, ioctl, ioctl.out ? outBuf : inBuf)); } Result INvDrvServices::Close(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { auto fd{request.Pop()}; state.logger->Debug("Closing NVDRV device ({})", fd); - driver->CloseDevice(fd); + driver.CloseDevice(fd); - response.Push(device::NvStatus::Success); - return {}; + return NVRESULT(NvResult::Success); } Result INvDrvServices::Initialize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - response.Push(device::NvStatus::Success); - return {}; + return NVRESULT(NvResult::Success); } Result INvDrvServices::QueryEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { auto fd{request.Pop()}; auto eventId{request.Pop()}; - auto device{driver->GetDevice(fd)}; - auto event{device->QueryEvent(eventId)}; + auto event{driver.QueryEvent(fd, eventId)}; if (event != nullptr) { auto handle{state.process->InsertItem(event)}; @@ -79,70 +79,16 @@ namespace skyline::service::nvdrv { state.logger->Debug("FD: {}, Event ID: {}, Handle: 0x{:X}", fd, eventId, handle); response.copyHandles.push_back(handle); - response.Push(device::NvStatus::Success); + return NVRESULT(NvResult::Success); } else { - response.Push(device::NvStatus::BadValue); + return NVRESULT(NvResult::BadValue); } - - return {}; } Result INvDrvServices::SetAruid(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - response.Push(device::NvStatus::Success); - return {}; + return NVRESULT(NvResult::Success); } - Result INvDrvServices::Ioctl2(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - auto fd{request.Pop()}; - auto cmd{request.Pop()}; - - auto device{driver->GetDevice(fd)}; - - // Strip the permissions from the command leaving only the ID - cmd &= 0xFFFF; - - if (request.inputBuf.size() < 2 || request.outputBuf.empty()) - throw exception("Inadequate amount of buffers for IOCTL2: I - {}, O - {}", request.inputBuf.size(), request.outputBuf.size()); - else if (request.inputBuf[0].data() != request.outputBuf[0].data()) - throw exception("IOCTL2 Input Buffer (0x{:X}) != Output Buffer (0x{:X}) [Input Buffer #2: 0x{:X}]", request.inputBuf[0].data(), request.outputBuf[0].data(), request.inputBuf[1].data()); - - span buffer{}; - if (request.inputBuf[0].size() >= request.outputBuf[0].size()) - buffer = request.inputBuf[0]; - else - buffer = request.outputBuf[0]; - - response.Push(device->HandleIoctl(cmd, device::IoctlType::Ioctl2, buffer, request.inputBuf[1])); - return {}; - } - - Result INvDrvServices::Ioctl3(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - auto fd{request.Pop()}; - auto cmd{request.Pop()}; - - auto device{driver->GetDevice(fd)}; - - // Strip the permissions from the command leaving only the ID - cmd &= 0xFFFF; - - if (request.inputBuf.empty() || request.outputBuf.empty()) { - throw exception("Inadequate amount of buffers for IOCTL3: I - {}, O - {}", request.inputBuf.size(), request.outputBuf.size()); - } else if (request.inputBuf[0].data() != request.outputBuf[0].data()) { - if (request.outputBuf.size() >= 2) - throw exception("IOCTL3 Input Buffer (0x{:X}) != Output Buffer (0x{:X}) [Output Buffer #2: 0x{:X}]", request.inputBuf[0].data(), request.outputBuf[0].data(), request.outputBuf[1].data()); - else - throw exception("IOCTL3 Input Buffer (0x{:X}) != Output Buffer (0x{:X})", request.inputBuf[0].data(), request.outputBuf[0].data()); - } - - span buffer{}; - if (request.inputBuf[0].size() >= request.outputBuf[0].size()) - buffer = request.inputBuf[0]; - else - buffer = request.outputBuf[0]; - - response.Push(device->HandleIoctl(cmd, device::IoctlType::Ioctl3, buffer, request.outputBuf.size() >= 2 ? request.outputBuf[1] : span())); - return {}; - } Result INvDrvServices::SetGraphicsFirmwareMemoryMarginEnabled(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { return {}; diff --git a/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.h b/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.h index bc7f609e..7378874f 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.h +++ b/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.h @@ -1,9 +1,10 @@ -// SPDX-License-Identifier: MPL-2.0 +// SPDX-License-Identifier: MIT OR MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #pragma once #include +#include "types.h" namespace skyline::service::nvdrv { class Driver; @@ -14,10 +15,13 @@ namespace skyline::service::nvdrv { */ class INvDrvServices : public BaseService { private: - std::shared_ptr driver; + Driver &driver; //!< The global nvdrv driver this session accesses + SessionContext ctx; //!< Session specific context + + FileDescriptor nextFdIndex{1}; //!< The index for the next allocated file descriptor public: - INvDrvServices(const DeviceState &state, ServiceManager &manager); + INvDrvServices(const DeviceState &state, ServiceManager &manager, Driver &driver, const SessionPermissions &perms); /** * @brief Open a specific device and return a FD @@ -59,13 +63,13 @@ namespace skyline::service::nvdrv { * @brief Perform an IOCTL on the specified FD with an extra input buffer * @url https://switchbrew.org/wiki/NV_services#Ioctl2 */ - Result Ioctl2(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + // Result Ioctl2(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); /** * @brief Perform an IOCTL on the specified FD with an extra output buffer * @url https://switchbrew.org/wiki/NV_services#Ioctl3 */ - Result Ioctl3(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + // Result Ioctl3(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); /** * @brief Enables the graphics firmware memory margin @@ -80,8 +84,7 @@ namespace skyline::service::nvdrv { SFUNC(0x3, INvDrvServices, Initialize), SFUNC(0x4, INvDrvServices, QueryEvent), SFUNC(0x8, INvDrvServices, SetAruid), - SFUNC(0xB, INvDrvServices, Ioctl2), - SFUNC(0xC, INvDrvServices, Ioctl3), + SFUNC(0xD, INvDrvServices, SetGraphicsFirmwareMemoryMarginEnabled) ) }; diff --git a/app/src/main/cpp/skyline/services/nvdrv/core/core.h b/app/src/main/cpp/skyline/services/nvdrv/core/core.h new file mode 100644 index 00000000..6fd57227 --- /dev/null +++ b/app/src/main/cpp/skyline/services/nvdrv/core/core.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT OR MPL-2.0 +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include "nvmap.h" +#include "syncpoint_manager.h" + +namespace skyline::service::nvdrv { + /** + * @brief Holds the global state of nvdrv + */ + struct Core { + core::NvMap nvMap; + core::SyncpointManager syncpointManager; + + Core(const DeviceState &state) : nvMap(state), syncpointManager(state) {} + }; +} \ No newline at end of file diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.cpp b/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.cpp index c9f5a702..1c2aca70 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.cpp +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.cpp @@ -1,11 +1,12 @@ -// SPDX-License-Identifier: MPL-2.0 -// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) +// SPDX-License-Identifier: MIT OR MPL-2.0 +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) #include -#include #include "nvdevice.h" namespace skyline::service::nvdrv::device { + NvDevice::NvDevice(const DeviceState &state, Core &core, const SessionContext &ctx) : state(state), core(core), ctx(ctx) {} + const std::string &NvDevice::GetName() { if (name.empty()) { auto mangledName{typeid(*this).name()}; @@ -18,32 +19,4 @@ namespace skyline::service::nvdrv::device { } return name; } - - NvStatus NvDevice::HandleIoctl(u32 cmd, IoctlType type, span buffer, span inlineBuffer) { - std::string_view typeString{[type] { - switch (type) { - case IoctlType::Ioctl: - return "IOCTL"; - case IoctlType::Ioctl2: - return "IOCTL2"; - case IoctlType::Ioctl3: - return "IOCTL3"; - } - }()}; - - NvDeviceFunctionDescriptor function; - try { - function = GetIoctlFunction(cmd); - state.logger->DebugNoPrefix("{}: {}", typeString, function.name); - } catch (std::out_of_range &) { - state.logger->Warn("Cannot find IOCTL for device '{}': 0x{:X}", GetName(), cmd); - return NvStatus::NotImplemented; - } - TRACE_EVENT("service", perfetto::StaticString{function.name}); - try { - return function(type, buffer, inlineBuffer); - } catch (const std::exception &e) { - throw exception("{} ({}: {})", e.what(), typeString, function.name); - } - } } diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.h b/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.h index 6ba45172..3ba436b3 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.h +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.h @@ -1,66 +1,19 @@ -// SPDX-License-Identifier: MPL-2.0 -// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) +// SPDX-License-Identifier: MIT OR MPL-2.0 +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) #pragma once #include #include +#include +#include +#include -#define NV_STRINGIFY(string) #string -#define NVFUNC(id, Class, Function) std::pair, span), const char*>>{id, {&Class::Function, NV_STRINGIFY(Class::Function)}} -#define NVDEVICE_DECL_AUTO(name, value) decltype(value) name = value -#define NVDEVICE_DECL(...) \ -NVDEVICE_DECL_AUTO(functions, frz::make_unordered_map({__VA_ARGS__})); \ -NvDeviceFunctionDescriptor GetIoctlFunction(u32 id) override { \ - auto& function{functions.at(id)}; \ - return NvDeviceFunctionDescriptor{ \ - reinterpret_cast(this), \ - reinterpret_cast(function.first), \ - function.second \ - }; \ -} +#include "deserialisation/types.h" namespace skyline::service::nvdrv::device { using namespace kernel; - - /** - * @brief All the possible error codes returned by the Nvidia driver - * @url https://switchbrew.org/wiki/NV_services#Errors - */ - enum class NvStatus : u32 { - Success = 0x0, //!< The operation has succeeded - NotImplemented = 0x1, //!< The operation is not implemented - NotSupported = 0x2, //!< The operation is not supported - NotInitialized = 0x3, //!< The operation uses an uninitialized object - BadParameter = 0x4, //!< The operation was provided a bad parameter - Timeout = 0x5, //!< The operation has timed out - InsufficientMemory = 0x6, //!< The device ran out of memory during the operation - ReadOnlyAttribute = 0x7, //!< The mutating operation was performed on a read only section - InvalidState = 0x8, //!< The state of the device was invalid - InvalidAddress = 0x9, //!< The provided address is invalid - InvalidSize = 0xA, //!< The provided size is invalid - BadValue = 0xB, //!< The operation was provided a bad value - AlreadyAllocated = 0xD, //!< An object was tried to be reallocated - Busy = 0xE, //!< The device is busy - ResourceError = 0xF, //!< There was an error accessing the resource - CountMismatch = 0x10, //!< ? - SharedMemoryTooSmall = 0x1000, //!< The shared memory segment is too small - FileOperationFailed = 0x30003, //!< The file operation has failed - DirOperationFailed = 0x30004, //!< The directory operation has failed - IoctlFailed = 0x3000F, //!< The IOCTL operation has failed - AccessDenied = 0x30010, //!< The access to a resource was denied - FileNotFound = 0x30013, //!< A file was not found - ModuleNotPresent = 0xA000E, //!< A module was not present - }; - - /** - * @brief The IOCTL call variants, they have different buffer configurations - */ - enum class IoctlType : u8 { - Ioctl, //!< 1 input/output buffer - Ioctl2, //!< 1 input/output buffer + 1 input buffer - Ioctl3, //!< 1 input/output buffer + 1 output buffer - }; + using namespace deserialisation; /** * @brief NvDevice is the base class that all /dev/nv* devices inherit from @@ -71,40 +24,25 @@ namespace skyline::service::nvdrv::device { protected: const DeviceState &state; - - class DerivedDevice; //!< A placeholder derived class which is used for class function semantics - - /** - * @brief A per-function descriptor for NvDevice functions - */ - struct NvDeviceFunctionDescriptor { - DerivedDevice *clazz; //!< A pointer to the class that this was derived from, it's used as the 'this' pointer for the function - NvStatus (DerivedDevice::*function)(IoctlType, span, span); //!< A function pointer to the implementation of the function - const char *name; //!< A pointer to a static string in the format "Class::Function" for the specific device class/function - - constexpr NvStatus operator()(IoctlType type, span buffer, span inlineBuffer) { - return (clazz->*function)(type, buffer, inlineBuffer); - } - }; + Core &core; + SessionContext ctx; public: - NvDevice(const DeviceState &state) : state(state) {} + NvDevice(const DeviceState &state, Core &core, const SessionContext &ctx); virtual ~NvDevice() = default; - virtual NvDeviceFunctionDescriptor GetIoctlFunction(u32 id) = 0; - /** * @return The name of the class * @note The lifetime of the returned string is tied to that of the class */ const std::string &GetName(); - /** - * @brief Handles IOCTL calls for devices - * @param cmd The IOCTL command that was called - */ - NvStatus HandleIoctl(u32 cmd, IoctlType type, span buffer, span inlineBuffer); + virtual PosixResult Ioctl(IoctlDescriptor cmd, span buffer) = 0; + + virtual PosixResult Ioctl2(IoctlDescriptor cmd, span buffer, span inlineOutput) { return PosixResult::NotSupported; } + + virtual PosixResult Ioctl3(IoctlDescriptor cmd, span buffer, span inlineInput) { return PosixResult::NotSupported; } virtual std::shared_ptr QueryEvent(u32 eventId) { return nullptr; diff --git a/app/src/main/cpp/skyline/services/nvdrv/driver.cpp b/app/src/main/cpp/skyline/services/nvdrv/driver.cpp index 061d49c7..3cdf8023 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/driver.cpp +++ b/app/src/main/cpp/skyline/services/nvdrv/driver.cpp @@ -1,61 +1,121 @@ -// SPDX-License-Identifier: MPL-2.0 +// SPDX-License-Identifier: MIT OR MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #include "driver.h" -#include "devices/nvhost_ctrl.h" -#include "devices/nvhost_ctrl_gpu.h" #include "devices/nvmap.h" -#include "devices/nvhost_channel.h" -#include "devices/nvhost_as_gpu.h" +#include "devices/nvhost/ctrl.h" +#include "devices/nvhost/ctrl_gpu.h" +#include "devices/nvhost/gpu_channel.h" +#include "devices/nvhost/as_gpu.h" + namespace skyline::service::nvdrv { - Driver::Driver(const DeviceState &state) : state(state), hostSyncpoint(state) {} + Driver::Driver(const DeviceState &state) : state(state), core(state) {} - u32 Driver::OpenDevice(std::string_view path) { - state.logger->Debug("Opening NVDRV device ({}): {}", fdIndex, path); + NvResult Driver::OpenDevice(std::string_view path, FileDescriptor fd, const SessionContext &ctx) { + state.logger->Debug("Opening NvDrv device ({}): {}", fd, path); + auto pathHash{util::Hash(path)}; - switch (util::Hash(path)) { - #define NVDEVICE(type, name, devicePath) \ - case util::Hash(devicePath): { \ - std::shared_ptr device{}; \ - if (name.expired()) { \ - device = std::make_shared(state); \ - name = device; \ - } else { \ - device = name.lock(); \ - } \ - devices.push_back(device); \ - break; \ - } - NVDEVICE_LIST - #undef NVDEVICE + #define DEVICE_SWITCH(cases) \ + switch (pathHash) { \ + cases; \ + default: \ + break; \ + } - default: - throw exception("Cannot find NVDRV device"); + #define DEVICE_CASE(path, object) \ + case util::Hash(path): \ + devices.emplace(fd, std::make_unique(state, core, ctx)); \ + return NvResult::Success; + + DEVICE_SWITCH( + DEVICE_CASE("/dev/nvmap", NvMap) + DEVICE_CASE("/dev/nvhost-ctrl", nvhost::Ctrl) + ); + + if (ctx.perms.AccessGpu) { + DEVICE_SWITCH( + DEVICE_CASE("/dev/nvhost-as-gpu", nvhost::AsGpu) + DEVICE_CASE("/dev/nvhost-ctrl-gpu", nvhost::CtrlGpu) + DEVICE_CASE("/dev/nvhost-gpu", nvhost::GpuChannel) + ); + }/* + + if (ctx.perms.AccessVic) { + switch (pathHash) { + ENTRY("/dev/nvhost-vic", nvhost::Vic) + default: + break; + } } - return fdIndex++; + if (ctx.perms.AccessVideoDecoder) { + switch (pathHash) { + ENTRY("/dev/nvhost-nvdec", nvhost::NvDec) + default: + break; + } + } + + if (ctx.perms.AccessJpeg) { + switch (pathHash) { + ENTRY("/dev/nvhost-nvjpg", nvhost::NvJpg) + default: + break; + } + }*/ + + #undef DEVICE_CASE + #undef DEVICE_SWITCH + + // Device doesn't exist/no permissions + return NvResult::FileOperationFailed; } - std::shared_ptr Driver::GetDevice(u32 fd) { + static NvResult ConvertResult(PosixResult result) { + switch (result) { + case PosixResult::Success: + return NvResult::Success; + case PosixResult::NotPermitted: + return NvResult::AccessDenied; + case PosixResult::TryAgain: + return NvResult::Timeout; + case PosixResult::Busy: + return NvResult::Busy; + case PosixResult::InvalidArgument: + return NvResult::BadValue; + case PosixResult::InappropriateIoctlForDevice: + return NvResult::IoctlFailed; + case PosixResult::NotSupported: + return NvResult::NotSupported; + case PosixResult::TimedOut: + return NvResult::Timeout; + default: + throw exception("Unhandled POSIX result: {}!", static_cast(result)); + } + + } + NvResult Driver::Ioctl(u32 fd, IoctlDescriptor cmd, span buffer) { try { - auto item{devices.at(fd)}; - if (!item) - throw exception("GetDevice was called with a closed file descriptor: 0x{:X}", fd); - return item; - } catch (std::out_of_range) { + return ConvertResult(devices.at(fd)->Ioctl(cmd, buffer)); + } catch (const std::out_of_range &) { throw exception("GetDevice was called with invalid file descriptor: 0x{:X}", fd); } } void Driver::CloseDevice(u32 fd) { try { - auto &device{devices.at(fd)}; - device.reset(); + devices.at(fd).reset(); } catch (const std::out_of_range &) { state.logger->Warn("Trying to close non-existent FD"); } } - std::weak_ptr driver{}; + std::shared_ptr Driver::QueryEvent(u32 fd, u32 eventId) { + try { + return devices.at(fd)->QueryEvent(eventId); + } catch (const std::exception &) { + throw exception("QueryEvent was called with invalid file descriptor: 0x{:X}", fd); + } + } } diff --git a/app/src/main/cpp/skyline/services/nvdrv/driver.h b/app/src/main/cpp/skyline/services/nvdrv/driver.h index 45cb0ab3..35284e40 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/driver.h +++ b/app/src/main/cpp/skyline/services/nvdrv/driver.h @@ -1,76 +1,45 @@ -// SPDX-License-Identifier: MPL-2.0 +// SPDX-License-Identifier: MIT OR MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #pragma once -#include "devices/nvhost_syncpoint.h" - -#define NVDEVICE_LIST \ - NVDEVICE(NvHostCtrl, nvHostCtrl, "/dev/nvhost-ctrl") \ - NVDEVICE(NvHostChannel, nvHostGpu, "/dev/nvhost-gpu") \ - NVDEVICE(NvHostChannel, nvHostNvdec, "/dev/nvhost-nvdec") \ - NVDEVICE(NvHostChannel, nvHostVic, "/dev/nvhost-vic") \ - NVDEVICE(NvMap, nvMap, "/dev/nvmap") \ - NVDEVICE(NvHostAsGpu, nvHostAsGpu, "/dev/nvhost-as-gpu") \ - NVDEVICE(NvHostCtrlGpu, nvHostCtrlGpu, "/dev/nvhost-ctrl-gpu") +#include +#include "types.h" +#include "devices/nvdevice.h" +#include "core/core.h" namespace skyline::service::nvdrv { - namespace device { - class NvDevice; - - #define NVDEVICE(type, name, path) class type; - NVDEVICE_LIST - #undef NVDEVICE - } - - /** - * @brief nvnflinger:dispdrv or nns::hosbinder::IHOSBinderDriver is responsible for writing buffers to the display - */ class Driver { private: const DeviceState &state; - std::vector> devices; //!< A vector of shared pointers to NvDevice object that correspond to FDs - u32 fdIndex{}; //!< The next file descriptor to assign + std::unordered_map> devices; public: - NvHostSyncpoint hostSyncpoint; - - #define NVDEVICE(type, name, path) std::weak_ptr name; - NVDEVICE_LIST - #undef NVDEVICE + Core core; //!< The core global state object of nvdrv that is accessed by devices Driver(const DeviceState &state); /** - * @brief Open a specific device and return a FD - * @param path The path of the device to open an FD to - * @return The file descriptor to the device + * @brief Creates a new device as specified by path + * @param path The /dev path that corresponds to the device + * @param fd The fd that will be used to refer to the device + * @param ctx The context to be attached to the device */ - u32 OpenDevice(std::string_view path); + NvResult OpenDevice(std::string_view path, FileDescriptor fd, const SessionContext &ctx); /** - * @brief Returns a particular device with a specific FD - * @param fd The file descriptor to retrieve - * @return A shared pointer to the device + * @brief Calls an IOCTL on the device specified by `fd` */ - std::shared_ptr GetDevice(u32 fd); + NvResult Ioctl(u32 fd, IoctlDescriptor cmd, span buffer); /** - * @brief Returns a particular device with a specific FD - * @tparam objectClass The class of the device to return - * @param fd The file descriptor to retrieve - * @return A shared pointer to the device + * @brief Queries a KEvent for the given `eventId` for the device specified by `fd` */ - template - std::shared_ptr GetDevice(u32 fd) { - return std::static_pointer_cast(GetDevice(fd)); - } + std::shared_ptr QueryEvent(u32 fd, u32 eventId); /** - * @brief Closes the specified device with its file descriptor + * @brief Closes the device specified by `fd` */ void CloseDevice(u32 fd); }; - - extern std::weak_ptr driver; //!< A globally shared instance of the Driver } diff --git a/app/src/main/cpp/skyline/services/nvdrv/types.h b/app/src/main/cpp/skyline/services/nvdrv/types.h new file mode 100644 index 00000000..9cf4976b --- /dev/null +++ b/app/src/main/cpp/skyline/services/nvdrv/types.h @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: MIT OR MPL-2.0 +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include + +namespace skyline::service::nvdrv { + using FileDescriptor = i32; + constexpr FileDescriptor InvalidFileDescriptor{-1}; + + /** + * @brief Holds the permissions for an nvdrv instance + */ + struct SessionPermissions { + bool AccessGpu; + bool AccessGpuDebug; + bool AccessGpuSchedule; + bool AccessVic; + bool AccessVideoEncoder; + bool AccessVideoDecoder; + bool AccessTsec; + bool AccessJpeg; + bool AccessDisplay; + bool AccessImportMemory; + bool NoCheckedAruid; + bool ModifyGraphicsMargin; + bool DuplicateNvMapHandles; + bool ExportNvMapHandles; + }; + + /** + * @brief Holds the per-session context for nvdrv + */ + struct SessionContext { + SessionPermissions perms; + bool internalSession; + }; + + /** + * @brief Permissions that applications have when using the 'nvdrv' service + */ + static constexpr SessionPermissions ApplicationSessionPermissions { + .AccessGpu = true, + .AccessGpuDebug = true, + .AccessVic = true, + .AccessVideoDecoder = true, + .ModifyGraphicsMargin = true + }; + + /** + * @brief An bitfield struct that unpacks an ioctl number, used as an alternative to Linux's macros + */ + union IoctlDescriptor { + struct { + u8 function; //!< The function number corresponding to a specific call in the driver + i8 magic; //!< Unique to each driver + u16 size : 14; //!< Size of the argument buffer + bool in : 1; //!< Guest is writing, we are reading + bool out : 1; //!< Guest is reading, we are writing + }; + + u32 raw; + }; + static_assert(sizeof(IoctlDescriptor) == sizeof(u32)); + + /** + * @brief NvRm result codes that are translated from the POSIX error codes used internally + * @url https://switchbrew.org/wiki/NV_services#NvError + */ + enum class NvResult : i32 { + Success = 0x0, + NotImplemented = 0x1, + NotSupported = 0x2, + NotInitialized = 0x3, + BadParameter = 0x4, + Timeout = 0x5, + InsufficientMemory = 0x6, + ReadOnlyAttribute = 0x7, + InvalidState = 0x8, + InvalidAddress = 0x9, + InvalidSize = 0xA, + BadValue = 0xB, + AlreadyAllocated = 0xD, + Busy = 0xE, + ResourceError = 0xF, + CountMismatch = 0x10, + Overflow = 0x11, + FileOperationFailed = 0x30003, + AccessDenied = 0x30010, + IoctlFailed = 0x3000F + }; +} diff --git a/app/src/main/cpp/skyline/services/serviceman.cpp b/app/src/main/cpp/skyline/services/serviceman.cpp index 3b63eef5..5736ea2c 100644 --- a/app/src/main/cpp/skyline/services/serviceman.cpp +++ b/app/src/main/cpp/skyline/services/serviceman.cpp @@ -18,7 +18,8 @@ #include "glue/IStaticService.h" #include "services/timesrv/core.h" #include "fssrv/IFileSystemProxy.h" -#include "services/nvdrv/INvDrvServices.h" +#include "nvdrv/INvDrvServices.h" +#include "nvdrv/driver.h" #include "hosbinder/IHOSBinderDriver.h" #include "visrv/IApplicationRootService.h" #include "visrv/ISystemRootService.h" @@ -48,8 +49,9 @@ namespace skyline::service { struct GlobalServiceState { timesrv::core::TimeServiceObject timesrv; + nvdrv::Driver nvdrv; - explicit GlobalServiceState(const DeviceState &state) : timesrv(state) {} + explicit GlobalServiceState(const DeviceState &state) : timesrv(state), nvdrv(state) {} }; ServiceManager::ServiceManager(const DeviceState &state) : state(state), smUserInterface(std::make_shared(state, *this)), globalServiceState(std::make_shared(state)) {} @@ -76,11 +78,8 @@ namespace skyline::service { SERVICE_CASE(glue::IStaticService, "time:r", globalServiceState->timesrv.managerServer.GetStaticServiceAsRepair(state, *this), globalServiceState->timesrv, timesrv::constant::StaticServiceRepairPermissions) SERVICE_CASE(glue::IStaticService, "time:u", globalServiceState->timesrv.managerServer.GetStaticServiceAsUser(state, *this), globalServiceState->timesrv, timesrv::constant::StaticServiceUserPermissions) SERVICE_CASE(fssrv::IFileSystemProxy, "fsp-srv") - SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv") - SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv:a") - SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv:s") - SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv:t") - SERVICE_CASE(hosbinder::IHOSBinderDriver, "dispdrv") + SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv", globalServiceState->nvdrv, nvdrv::ApplicationSessionPermissions) + SERVICE_CASE(hosbinder::IHOSBinderDriver, "dispdrv", globalServiceState->nvdrv.core.nvMap) SERVICE_CASE(visrv::IApplicationRootService, "vi:u") SERVICE_CASE(visrv::ISystemRootService, "vi:s") SERVICE_CASE(visrv::IManagerRootService, "vi:m")