mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-26 22:14:17 +01:00
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.
This commit is contained in:
parent
db2abe2290
commit
a19bf973b1
@ -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 <kernel/types/KProcess.h>
|
||||
@ -6,72 +6,72 @@
|
||||
#include "driver.h"
|
||||
#include "devices/nvdevice.h"
|
||||
|
||||
#define NVRESULT(x) [&response](NvResult err) { response.Push<NvResult>(err); return Result{}; }(x)
|
||||
|
||||
namespace skyline::service::nvdrv {
|
||||
INvDrvServices::INvDrvServices(const DeviceState &state, ServiceManager &manager) : driver(nvdrv::driver.expired() ? std::make_shared<Driver>(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<u32>(driver->OpenDevice(path));
|
||||
response.Push(device::NvStatus::Success);
|
||||
auto path{request.inputBuf.at(0).as_string(true)};
|
||||
if (path.empty() || nextFdIndex == SessionFdLimit) {
|
||||
response.Push<FileDescriptor>(InvalidFileDescriptor);
|
||||
return NVRESULT(NvResult::FileOperationFailed);
|
||||
}
|
||||
|
||||
return {};
|
||||
if (auto err{driver.OpenDevice(path, nextFdIndex, ctx)}; err != NvResult::Success) {
|
||||
response.Push<FileDescriptor>(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<u32>()};
|
||||
auto cmd{request.Pop<u32>()};
|
||||
auto fd{request.Pop<FileDescriptor>()};
|
||||
auto ioctl{request.Pop<IoctlDescriptor>()};
|
||||
|
||||
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<u8> 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<u32>()};
|
||||
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<u32>()};
|
||||
auto eventId{request.Pop<u32>()};
|
||||
|
||||
auto device{driver->GetDevice(fd)};
|
||||
auto event{device->QueryEvent(eventId)};
|
||||
auto event{driver.QueryEvent(fd, eventId)};
|
||||
|
||||
if (event != nullptr) {
|
||||
auto handle{state.process->InsertItem<type::KEvent>(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<u32>()};
|
||||
auto cmd{request.Pop<u32>()};
|
||||
|
||||
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<u8> 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<u32>()};
|
||||
auto cmd{request.Pop<u32>()};
|
||||
|
||||
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<u8> 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<u8>()));
|
||||
return {};
|
||||
}
|
||||
|
||||
Result INvDrvServices::SetGraphicsFirmwareMemoryMarginEnabled(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
|
@ -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 <services/serviceman.h>
|
||||
#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 &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)
|
||||
)
|
||||
};
|
||||
|
19
app/src/main/cpp/skyline/services/nvdrv/core/core.h
Normal file
19
app/src/main/cpp/skyline/services/nvdrv/core/core.h
Normal file
@ -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) {}
|
||||
};
|
||||
}
|
@ -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 <cxxabi.h>
|
||||
#include <common/trace.h>
|
||||
#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<u8> buffer, span<u8> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 <kernel/ipc.h>
|
||||
#include <kernel/types/KEvent.h>
|
||||
#include <services/common/result.h>
|
||||
#include <services/nvdrv/types.h>
|
||||
#include <services/nvdrv/core/core.h>
|
||||
|
||||
#define NV_STRINGIFY(string) #string
|
||||
#define NVFUNC(id, Class, Function) std::pair<u32, std::pair<NvStatus(Class::*)(IoctlType, span<u8>, span<u8>), 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<DerivedDevice*>(this), \
|
||||
reinterpret_cast<decltype(NvDeviceFunctionDescriptor::function)>(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<u8>, span<u8>); //!< 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<u8> buffer, span<u8> 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<u8> buffer, span<u8> inlineBuffer);
|
||||
virtual PosixResult Ioctl(IoctlDescriptor cmd, span<u8> buffer) = 0;
|
||||
|
||||
virtual PosixResult Ioctl2(IoctlDescriptor cmd, span<u8> buffer, span<u8> inlineOutput) { return PosixResult::NotSupported; }
|
||||
|
||||
virtual PosixResult Ioctl3(IoctlDescriptor cmd, span<u8> buffer, span<u8> inlineInput) { return PosixResult::NotSupported; }
|
||||
|
||||
virtual std::shared_ptr<kernel::type::KEvent> QueryEvent(u32 eventId) {
|
||||
return nullptr;
|
||||
|
@ -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::type> device{}; \
|
||||
if (name.expired()) { \
|
||||
device = std::make_shared<device::type>(state); \
|
||||
name = device; \
|
||||
} else { \
|
||||
device = name.lock(); \
|
||||
} \
|
||||
devices.push_back(device); \
|
||||
#define DEVICE_SWITCH(cases) \
|
||||
switch (pathHash) { \
|
||||
cases; \
|
||||
default: \
|
||||
break; \
|
||||
}
|
||||
NVDEVICE_LIST
|
||||
#undef NVDEVICE
|
||||
|
||||
#define DEVICE_CASE(path, object) \
|
||||
case util::Hash(path): \
|
||||
devices.emplace(fd, std::make_unique<device::object>(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:
|
||||
throw exception("Cannot find NVDRV device");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return fdIndex++;
|
||||
if (ctx.perms.AccessVideoDecoder) {
|
||||
switch (pathHash) {
|
||||
ENTRY("/dev/nvhost-nvdec", nvhost::NvDec)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<device::NvDevice> Driver::GetDevice(u32 fd) {
|
||||
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;
|
||||
}
|
||||
|
||||
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<i32>(result));
|
||||
}
|
||||
|
||||
}
|
||||
NvResult Driver::Ioctl(u32 fd, IoctlDescriptor cmd, span<u8> 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> driver{};
|
||||
std::shared_ptr<kernel::type::KEvent> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 <common.h>
|
||||
#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<std::shared_ptr<device::NvDevice>> devices; //!< A vector of shared pointers to NvDevice object that correspond to FDs
|
||||
u32 fdIndex{}; //!< The next file descriptor to assign
|
||||
std::unordered_map<FileDescriptor, std::unique_ptr<device::NvDevice>> devices;
|
||||
|
||||
public:
|
||||
NvHostSyncpoint hostSyncpoint;
|
||||
|
||||
#define NVDEVICE(type, name, path) std::weak_ptr<device::type> 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<device::NvDevice> GetDevice(u32 fd);
|
||||
NvResult Ioctl(u32 fd, IoctlDescriptor cmd, span<u8> 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<typename objectClass>
|
||||
std::shared_ptr<objectClass> GetDevice(u32 fd) {
|
||||
return std::static_pointer_cast<objectClass>(GetDevice(fd));
|
||||
}
|
||||
std::shared_ptr<kernel::type::KEvent> 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> driver; //!< A globally shared instance of the Driver
|
||||
}
|
||||
|
93
app/src/main/cpp/skyline/services/nvdrv/types.h
Normal file
93
app/src/main/cpp/skyline/services/nvdrv/types.h
Normal file
@ -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 <common.h>
|
||||
|
||||
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
|
||||
};
|
||||
}
|
@ -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<sm::IUserInterface>(state, *this)), globalServiceState(std::make_shared<GlobalServiceState>(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")
|
||||
|
Loading…
Reference in New Issue
Block a user