mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 17:51:52 +01:00
NVDRV IOCTL Refactor
Buffer -> Span + All buffers as arguments + Return -> NvStatus + Print Service Names + Function Names
This commit is contained in:
parent
70d67ef563
commit
a5fece8020
3
app/proguard-rules.pro
vendored
3
app/proguard-rules.pro
vendored
@ -1,3 +1,6 @@
|
|||||||
# Skyline Proguard Rules
|
# Skyline Proguard Rules
|
||||||
# For more details, see
|
# For more details, see
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# Retain all classes within Skyline for traces + JNI access + Serializable classes
|
||||||
|
-keep class emu.skyline.*
|
||||||
|
@ -187,7 +187,7 @@ namespace skyline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<size_t Size>
|
template<size_t Size>
|
||||||
constexpr std::array<u8, Size> HexStringToArray(const std::string_view &hexString) {
|
constexpr std::array<u8, Size> HexStringToArray(std::string_view hexString) {
|
||||||
if (hexString.size() != Size * 2)
|
if (hexString.size() != Size * 2)
|
||||||
throw exception("Invalid size");
|
throw exception("Invalid size");
|
||||||
std::array<u8, Size> result;
|
std::array<u8, Size> result;
|
||||||
@ -198,9 +198,23 @@ namespace skyline {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::size_t Hash(const std::string_view& view) {
|
constexpr std::size_t Hash(std::string_view view) {
|
||||||
return frz::elsa<frz::string>{}(frz::string(view.data(), view.size()), 0);
|
return frz::elsa<frz::string>{}(frz::string(view.data(), view.size()), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Out, typename In>
|
||||||
|
constexpr Out& As(const std::span<In> &span) {
|
||||||
|
if (span.size_bytes() < sizeof(Out))
|
||||||
|
throw exception("Span size less than Out type size");
|
||||||
|
return *reinterpret_cast<Out*>(span.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Out, typename In>
|
||||||
|
constexpr std::span<Out> AsSpan(const std::span<In> &span) {
|
||||||
|
if (span.size_bytes() < sizeof(Out))
|
||||||
|
throw exception("Span size less than Out type size");
|
||||||
|
return std::span(reinterpret_cast<Out*>(span.data()), span.size_bytes() / sizeof(Out));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,7 +33,6 @@ namespace skyline::service::nvdrv {
|
|||||||
Result INvDrvServices::Ioctl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
Result INvDrvServices::Ioctl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
auto fd = request.Pop<u32>();
|
auto fd = request.Pop<u32>();
|
||||||
auto cmd = request.Pop<u32>();
|
auto cmd = request.Pop<u32>();
|
||||||
state.logger->Debug("IOCTL on device: 0x{:X}, cmd: 0x{:X}", fd, cmd);
|
|
||||||
|
|
||||||
auto device = driver->GetDevice(fd);
|
auto device = driver->GetDevice(fd);
|
||||||
|
|
||||||
@ -41,29 +40,25 @@ namespace skyline::service::nvdrv {
|
|||||||
cmd &= 0xFFFF;
|
cmd &= 0xFFFF;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
std::optional<kernel::ipc::IpcBuffer> buffer{std::nullopt};
|
||||||
if (request.inputBuf.empty() || request.outputBuf.empty()) {
|
if (request.inputBuf.empty() || request.outputBuf.empty()) {
|
||||||
if (request.inputBuf.empty()) {
|
if (!request.inputBuf.empty())
|
||||||
device::IoctlData data(request.outputBuf.at(0));
|
buffer = request.inputBuf.at(0);
|
||||||
|
else if (!request.outputBuf.empty())
|
||||||
device->HandleIoctl(cmd, data);
|
buffer = request.outputBuf.at(0);
|
||||||
response.Push(data.status);
|
else
|
||||||
|
throw exception("No IOCTL Buffers");
|
||||||
|
} else if (request.inputBuf.at(0).address == request.outputBuf.at(0).address) {
|
||||||
|
buffer = request.inputBuf.at(0);
|
||||||
} else {
|
} else {
|
||||||
device::IoctlData data(request.inputBuf.at(0));
|
throw exception("IOCTL Input Buffer != Output Buffer");
|
||||||
|
|
||||||
device->HandleIoctl(cmd, data);
|
|
||||||
response.Push(data.status);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
device::IoctlData data(request.inputBuf.at(0), request.outputBuf.at(0));
|
|
||||||
|
|
||||||
device->HandleIoctl(cmd, data);
|
|
||||||
response.Push(data.status);
|
|
||||||
}
|
|
||||||
} catch (const std::out_of_range &) {
|
|
||||||
throw exception("IOCTL was requested on an invalid file descriptor");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
response.Push(device->HandleIoctl(cmd, device::IoctlType::Ioctl, std::span<u8>(reinterpret_cast<u8 *>(buffer->address), buffer->size), {}));
|
||||||
return {};
|
return {};
|
||||||
|
} catch (const std::out_of_range &) {
|
||||||
|
throw exception("IOCTL was requested on an invalid file descriptor: 0x{:X}", fd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result INvDrvServices::Close(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
Result INvDrvServices::Close(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
|
@ -4,29 +4,33 @@
|
|||||||
#include "nvdevice.h"
|
#include "nvdevice.h"
|
||||||
|
|
||||||
namespace skyline::service::nvdrv::device {
|
namespace skyline::service::nvdrv::device {
|
||||||
std::string NvDevice::GetName() {
|
const std::string &NvDevice::GetName() {
|
||||||
|
if (name.empty()) {
|
||||||
|
auto mangledName = typeid(*this).name();
|
||||||
|
|
||||||
int status{};
|
int status{};
|
||||||
size_t length{};
|
size_t length{};
|
||||||
auto mangledName{typeid(*this).name()};
|
|
||||||
|
|
||||||
std::unique_ptr<char, decltype(&std::free)> demangled{abi::__cxa_demangle(mangledName, nullptr, &length, &status), std::free};
|
std::unique_ptr<char, decltype(&std::free)> demangled{abi::__cxa_demangle(mangledName, nullptr, &length, &status), std::free};
|
||||||
|
|
||||||
return (status == 0) ? std::string(demangled.get() + std::char_traits<char>::length("skyline::service::nvdrv::device")) : mangledName;
|
name = (status == 0) ? std::string(demangled.get() + std::char_traits<char>::length("skyline::service::nvdrv::device::")) : mangledName;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvDevice::HandleIoctl(u32 cmd, IoctlData &input) {
|
NvStatus NvDevice::HandleIoctl(u32 cmd, IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
std::function<void(IoctlData &)> function;
|
std::pair<std::function<NvStatus(IoctlType, std::span<u8>, std::span<u8>)>, std::string_view> function;
|
||||||
try {
|
try {
|
||||||
function = GetServiceFunction(cmd);
|
function = GetIoctlFunction(cmd);
|
||||||
|
state.logger->Debug("IOCTL @ {}: {}", GetName(), function.second);
|
||||||
} catch (std::out_of_range &) {
|
} catch (std::out_of_range &) {
|
||||||
state.logger->Warn("Cannot find IOCTL for device '{}': 0x{:X}", GetName(), cmd);
|
state.logger->Warn("Cannot find IOCTL for device '{}': 0x{:X}", GetName(), cmd);
|
||||||
input.status = NvStatus::NotImplemented;
|
return NvStatus::NotImplemented;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
function(input);
|
return function.first(type, buffer, inlineBuffer);
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) {
|
||||||
throw exception("{} (Device: {})", e.what(), GetName());
|
throw exception("{} (Device: {})", e.what(), GetName());
|
||||||
}
|
}
|
||||||
|
exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,13 @@
|
|||||||
#include <kernel/ipc.h>
|
#include <kernel/ipc.h>
|
||||||
#include <kernel/types/KEvent.h>
|
#include <kernel/types/KEvent.h>
|
||||||
|
|
||||||
#define NVFUNC(id, Class, Function) std::pair<u32, std::function<void(Class*, IoctlData &)>>(id, &Class::Function)
|
#define NVFUNC(id, Class, Function) std::pair<u32, std::pair<std::function<NvStatus(Class*, IoctlType, std::span<u8>, std::span<u8>)>, std::string_view>>{id, {&Class::Function, #Function}}
|
||||||
#define NVDEVICE_DECL_AUTO(name, value) decltype(value) name = value
|
#define NVDEVICE_DECL_AUTO(name, value) decltype(value) name = value
|
||||||
#define NVDEVICE_DECL(...) \
|
#define NVDEVICE_DECL(...) \
|
||||||
NVDEVICE_DECL_AUTO(functions, frz::make_unordered_map({__VA_ARGS__})); \
|
NVDEVICE_DECL_AUTO(functions, frz::make_unordered_map({__VA_ARGS__})); \
|
||||||
std::function<void(IoctlData &)> GetServiceFunction(u32 index) { \
|
std::pair<std::function<NvStatus(IoctlType, std::span<u8>, std::span<u8>)>, std::string_view> GetIoctlFunction(u32 id) { \
|
||||||
return std::bind(functions.at(index), this, std::placeholders::_1); \
|
auto& function = functions.at(id); \
|
||||||
|
return std::make_pair(std::bind(function.first, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), function.second); \
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace skyline::service::nvdrv::device {
|
namespace skyline::service::nvdrv::device {
|
||||||
@ -49,53 +50,21 @@ namespace skyline::service::nvdrv::device {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This holds all the input and output data for an IOCTL function
|
* @brief The IOCTL call variants, they have different buffer configurations
|
||||||
*/
|
*/
|
||||||
struct IoctlData {
|
enum class IoctlType : u8 {
|
||||||
std::vector<kernel::ipc::InputBuffer> input; //!< A vector of all input IOCTL buffers
|
Ioctl, //!< 1 input/output buffer
|
||||||
std::vector<kernel::ipc::OutputBuffer> output; //!< A vector of all output IOCTL buffers
|
Ioctl2, //!< 1 input/output buffer + 1 input buffer
|
||||||
NvStatus status{NvStatus::Success}; //!< The error code that is returned to the application
|
Ioctl3, //!< 1 input/output buffer + 1 output buffer
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This constructor takes 1 input buffer and 1 output buffer, it's used for Ioctl
|
|
||||||
* @param input An input buffer
|
|
||||||
* @param output An output buffer
|
|
||||||
*/
|
|
||||||
IoctlData(kernel::ipc::InputBuffer input, kernel::ipc::OutputBuffer output) : input({input}), output({output}) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This constructor takes 1 input buffer, it's used for Ioctl sometimes
|
|
||||||
* @param output An output buffer
|
|
||||||
*/
|
|
||||||
IoctlData(kernel::ipc::InputBuffer input) : input({input}) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This constructor takes 1 output buffer, it's used for Ioctl sometimes
|
|
||||||
* @param output An output buffer
|
|
||||||
*/
|
|
||||||
IoctlData(kernel::ipc::OutputBuffer output) : output({output}) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This constructor takes 2 input buffers and 1 output buffer, it's used for Ioctl1
|
|
||||||
* @param input1 The first input buffer
|
|
||||||
* @param input2 The second input buffer
|
|
||||||
* @param output An output buffer
|
|
||||||
*/
|
|
||||||
IoctlData(kernel::ipc::InputBuffer input1, kernel::ipc::InputBuffer input2, kernel::ipc::OutputBuffer output) : input({input1, input2}), output({output}) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This constructor takes 1 input buffer and 2 output buffers, it's used for Ioctl2
|
|
||||||
* @param input An input buffer
|
|
||||||
* @param output1 The first output buffer
|
|
||||||
* @param output2 The second output buffer
|
|
||||||
*/
|
|
||||||
IoctlData(kernel::ipc::InputBuffer input, kernel::ipc::OutputBuffer output1, kernel::ipc::OutputBuffer output2) : input({input}), output({output1, output2}) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief NvDevice is the base class that all /dev/nv* devices inherit from
|
* @brief NvDevice is the base class that all /dev/nv* devices inherit from
|
||||||
*/
|
*/
|
||||||
class NvDevice {
|
class NvDevice {
|
||||||
|
private:
|
||||||
|
std::string name; //!< The name of the device
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const DeviceState &state; //!< The state of the device
|
const DeviceState &state; //!< The state of the device
|
||||||
|
|
||||||
@ -104,19 +73,19 @@ namespace skyline::service::nvdrv::device {
|
|||||||
|
|
||||||
virtual ~NvDevice() = default;
|
virtual ~NvDevice() = default;
|
||||||
|
|
||||||
virtual std::function<void(IoctlData &)> GetServiceFunction(u32 index) = 0;
|
virtual std::pair<std::function<NvStatus(IoctlType, std::span<u8>, std::span<u8>)>, std::string_view> GetIoctlFunction(u32 id) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The name of the class
|
* @return The name of the class
|
||||||
|
* @note The lifetime of the returned string is tied to that of the class
|
||||||
*/
|
*/
|
||||||
std::string GetName();
|
const std::string& GetName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This handles IOCTL calls for devices
|
* @brief This handles IOCTL calls for devices
|
||||||
* @param cmd The IOCTL command that was called
|
* @param cmd The IOCTL command that was called
|
||||||
* @param input The input to the IOCTL call
|
|
||||||
*/
|
*/
|
||||||
void HandleIoctl(u32 cmd, IoctlData &input);
|
NvStatus HandleIoctl(u32 cmd, IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
inline virtual std::shared_ptr<kernel::type::KEvent> QueryEvent(u32 eventId) {
|
inline virtual std::shared_ptr<kernel::type::KEvent> QueryEvent(u32 eventId) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -11,20 +11,21 @@
|
|||||||
namespace skyline::service::nvdrv::device {
|
namespace skyline::service::nvdrv::device {
|
||||||
NvHostAsGpu::NvHostAsGpu(const DeviceState &state) : NvDevice(state) {}
|
NvHostAsGpu::NvHostAsGpu(const DeviceState &state) : NvDevice(state) {}
|
||||||
|
|
||||||
void NvHostAsGpu::BindChannel(IoctlData &buffer) {
|
NvStatus NvHostAsGpu::BindChannel(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
struct Data {
|
return NvStatus::Success;
|
||||||
u32 fd;
|
|
||||||
} &channelInfo = state.process->GetReference<Data>(buffer.input.at(0).address);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostAsGpu::AllocSpace(IoctlData &buffer) {
|
NvStatus NvHostAsGpu::AllocSpace(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
struct Data {
|
struct Data {
|
||||||
u32 pages;
|
u32 pages; // In
|
||||||
u32 pageSize;
|
u32 pageSize; // In
|
||||||
u32 flags;
|
u32 flags; // In
|
||||||
u32 _pad_;
|
u32 _pad_;
|
||||||
u64 offset;
|
union {
|
||||||
} region = state.process->GetObject<Data>(buffer.input.at(0).address);
|
u64 offset; // InOut
|
||||||
|
u64 align; // In
|
||||||
|
};
|
||||||
|
} region = util::As<Data>(buffer);
|
||||||
|
|
||||||
u64 size = static_cast<u64>(region.pages) * static_cast<u64>(region.pageSize);
|
u64 size = static_cast<u64>(region.pages) * static_cast<u64>(region.pageSize);
|
||||||
|
|
||||||
@ -35,57 +36,62 @@ namespace skyline::service::nvdrv::device {
|
|||||||
|
|
||||||
if (region.offset == 0) {
|
if (region.offset == 0) {
|
||||||
state.logger->Warn("Failed to allocate GPU address space region!");
|
state.logger->Warn("Failed to allocate GPU address space region!");
|
||||||
buffer.status = NvStatus::BadParameter;
|
return NvStatus::BadParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.process->WriteMemory(region, buffer.output.at(0).address);
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostAsGpu::UnmapBuffer(IoctlData &buffer) {
|
NvStatus NvHostAsGpu::UnmapBuffer(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
auto offset = state.process->GetObject<u64>(buffer.input.at(0).address);
|
u64 offset{util::As<u64>(buffer)};
|
||||||
|
|
||||||
if (!state.gpu->memoryManager.Unmap(offset))
|
if (!state.gpu->memoryManager.Unmap(offset))
|
||||||
state.logger->Warn("Failed to unmap chunk at 0x{:X}", offset);
|
state.logger->Warn("Failed to unmap chunk at 0x{:X}", offset);
|
||||||
|
|
||||||
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostAsGpu::Modify(IoctlData &buffer) {
|
NvStatus NvHostAsGpu::Modify(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
struct Data {
|
struct Data {
|
||||||
u32 flags;
|
u32 flags; // In
|
||||||
u32 kind;
|
u32 kind; // In
|
||||||
u32 nvmapHandle;
|
u32 nvmapHandle; // In
|
||||||
u32 pageSize;
|
u32 pageSize; // InOut
|
||||||
u64 bufferOffset;
|
u64 bufferOffset; // In
|
||||||
u64 mappingSize;
|
u64 mappingSize; // In
|
||||||
u64 offset;
|
u64 offset; // InOut
|
||||||
} region = state.process->GetObject<Data>(buffer.input.at(0).address);
|
} &data = util::As<Data>(buffer);
|
||||||
|
|
||||||
if (!region.nvmapHandle)
|
|
||||||
return;
|
|
||||||
|
|
||||||
|
try {
|
||||||
auto driver = nvdrv::driver.lock();
|
auto driver = nvdrv::driver.lock();
|
||||||
auto nvmap = driver->nvMap.lock();
|
auto nvmap = driver->nvMap.lock();
|
||||||
auto mapping = nvmap->handleTable.at(region.nvmapHandle);
|
auto mapping = nvmap->handleTable.at(data.nvmapHandle);
|
||||||
|
|
||||||
u64 mapPhysicalAddress = region.bufferOffset + mapping->address;
|
u64 mapPhysicalAddress = data.bufferOffset + mapping->address;
|
||||||
u64 mapSize = region.mappingSize ? region.mappingSize : mapping->size;
|
u64 mapSize = data.mappingSize ? data.mappingSize : mapping->size;
|
||||||
|
|
||||||
if (region.flags & 1)
|
if (data.flags & 1)
|
||||||
region.offset = state.gpu->memoryManager.MapFixed(region.offset, mapPhysicalAddress, mapSize);
|
data.offset = state.gpu->memoryManager.MapFixed(data.offset, mapPhysicalAddress, mapSize);
|
||||||
else
|
else
|
||||||
region.offset = state.gpu->memoryManager.MapAllocate(mapPhysicalAddress, mapSize);
|
data.offset = state.gpu->memoryManager.MapAllocate(mapPhysicalAddress, mapSize);
|
||||||
|
|
||||||
if (region.offset == 0) {
|
if (data.offset == 0) {
|
||||||
state.logger->Warn("Failed to map GPU address space region!");
|
state.logger->Warn("Failed to map GPU address space region!");
|
||||||
buffer.status = NvStatus::BadParameter;
|
return NvStatus::BadParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.process->WriteMemory(region, buffer.output.at(0).address);
|
return NvStatus::Success;
|
||||||
|
} catch (const std::out_of_range &) {
|
||||||
|
state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.nvmapHandle);
|
||||||
|
return NvStatus::BadParameter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostAsGpu::GetVaRegions(IoctlData &buffer) {
|
NvStatus NvHostAsGpu::GetVaRegions(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
|
/*
|
||||||
struct Data {
|
struct Data {
|
||||||
u64 _pad0_;
|
u64 _pad0_;
|
||||||
u32 bufferSize;
|
u32 bufferSize; // InOut
|
||||||
u32 _pad1_;
|
u32 _pad1_;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@ -93,38 +99,40 @@ namespace skyline::service::nvdrv::device {
|
|||||||
u32 page_size;
|
u32 page_size;
|
||||||
u32 pad;
|
u32 pad;
|
||||||
u64 pages;
|
u64 pages;
|
||||||
} regions[2];
|
} regions[2]; // Out
|
||||||
} ®ionInfo = state.process->GetReference<Data>(buffer.input.at(0).address);
|
} ®ionInfo = util::As<Data>(buffer);
|
||||||
state.process->WriteMemory(regionInfo, buffer.output.at(0).address);
|
*/
|
||||||
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostAsGpu::InitializeEx(IoctlData &buffer) {
|
NvStatus NvHostAsGpu::AllocAsEx(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
|
/*
|
||||||
struct Data {
|
struct Data {
|
||||||
u32 bigPageSize;
|
u32 bigPageSize; // In
|
||||||
i32 asFd;
|
i32 asFd; // In
|
||||||
u32 flags;
|
u32 flags; // In
|
||||||
u32 reserved;
|
u32 reserved; // In
|
||||||
u64 vaRangeStart;
|
u64 vaRangeStart; // In
|
||||||
u64 vaRangeEnd;
|
u64 vaRangeEnd; // In
|
||||||
u64 vaRangeSplit;
|
u64 vaRangeSplit; // In
|
||||||
} addressSpace = state.process->GetObject<Data>(buffer.input.at(0).address);
|
} addressSpace = util::As<Data>(buffer);
|
||||||
|
*/
|
||||||
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostAsGpu::Remap(IoctlData &buffer) {
|
NvStatus NvHostAsGpu::Remap(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
struct Entry {
|
struct Entry {
|
||||||
u16 flags;
|
u16 flags; // In
|
||||||
u16 kind;
|
u16 kind; // In
|
||||||
u32 nvmapHandle;
|
u32 nvmapHandle; // In
|
||||||
u32 mapOffset;
|
u32 mapOffset; // In
|
||||||
u32 gpuOffset;
|
u32 gpuOffset; // In
|
||||||
u32 pages;
|
u32 pages; // In
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr u32 MinAlignmentShift{0x10}; // This shift is applied to all addresses passed to Remap
|
constexpr u32 MinAlignmentShift{0x10}; // This shift is applied to all addresses passed to Remap
|
||||||
|
|
||||||
size_t entryCount{buffer.input.at(0).size / sizeof(Entry)};
|
auto entries{util::AsSpan<Entry>(buffer)};
|
||||||
std::span entries(state.process->GetPointer<Entry>(buffer.input.at(0).address), entryCount);
|
|
||||||
|
|
||||||
for (auto entry : entries) {
|
for (auto entry : entries) {
|
||||||
try {
|
try {
|
||||||
auto driver = nvdrv::driver.lock();
|
auto driver = nvdrv::driver.lock();
|
||||||
@ -136,10 +144,12 @@ namespace skyline::service::nvdrv::device {
|
|||||||
u64 mapSize = static_cast<u64>(entry.pages) << MinAlignmentShift;
|
u64 mapSize = static_cast<u64>(entry.pages) << MinAlignmentShift;
|
||||||
|
|
||||||
state.gpu->memoryManager.MapFixed(mapAddress, mapPhysicalAddress, mapSize);
|
state.gpu->memoryManager.MapFixed(mapAddress, mapPhysicalAddress, mapSize);
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::out_of_range &) {
|
||||||
buffer.status = NvStatus::BadValue;
|
state.logger->Warn("Invalid NvMap handle: 0x{:X}", entry.nvmapHandle);
|
||||||
return;
|
return NvStatus::BadParameter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,37 +16,37 @@ namespace skyline::service::nvdrv::device {
|
|||||||
/**
|
/**
|
||||||
* @brief This binds a channel to the address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_BIND_CHANNEL)
|
* @brief This binds a channel to the address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_BIND_CHANNEL)
|
||||||
*/
|
*/
|
||||||
void BindChannel(IoctlData &buffer);
|
NvStatus BindChannel(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This reserves a region in the GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_ALLOC_SPACE)
|
* @brief This reserves a region in the GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_ALLOC_SPACE)
|
||||||
*/
|
*/
|
||||||
void AllocSpace(IoctlData &buffer);
|
NvStatus AllocSpace(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This unmaps a region in the GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_UNMAP_BUFFER)
|
* @brief This unmaps a region in the GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_UNMAP_BUFFER)
|
||||||
*/
|
*/
|
||||||
void UnmapBuffer(IoctlData &buffer);
|
NvStatus UnmapBuffer(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This maps a region in the GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_MODIFY)
|
* @brief This maps a region in the GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_MODIFY)
|
||||||
*/
|
*/
|
||||||
void Modify(IoctlData &buffer);
|
NvStatus Modify(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This returns the application's GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_GET_VA_REGIONS)
|
* @brief This returns the application's GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_GET_VA_REGIONS)
|
||||||
*/
|
*/
|
||||||
void GetVaRegions(IoctlData &buffer);
|
NvStatus GetVaRegions(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This initializes the application's GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_INITIALIZE_EX)
|
* @brief This initializes the application's GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_ALLOC_AS_EX)
|
||||||
*/
|
*/
|
||||||
void InitializeEx(IoctlData &buffer);
|
NvStatus AllocAsEx(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Remaps a region of the GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_REMAP)
|
* @brief Remaps a region of the GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_REMAP)
|
||||||
*/
|
*/
|
||||||
void Remap(IoctlData &buffer);
|
NvStatus Remap(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
NVDEVICE_DECL(
|
NVDEVICE_DECL(
|
||||||
NVFUNC(0x4101, NvHostAsGpu, BindChannel),
|
NVFUNC(0x4101, NvHostAsGpu, BindChannel),
|
||||||
@ -54,7 +54,7 @@ namespace skyline::service::nvdrv::device {
|
|||||||
NVFUNC(0x4105, NvHostAsGpu, UnmapBuffer),
|
NVFUNC(0x4105, NvHostAsGpu, UnmapBuffer),
|
||||||
NVFUNC(0x4106, NvHostAsGpu, Modify),
|
NVFUNC(0x4106, NvHostAsGpu, Modify),
|
||||||
NVFUNC(0x4108, NvHostAsGpu, GetVaRegions),
|
NVFUNC(0x4108, NvHostAsGpu, GetVaRegions),
|
||||||
NVFUNC(0x4109, NvHostAsGpu, InitializeEx),
|
NVFUNC(0x4109, NvHostAsGpu, AllocAsEx),
|
||||||
NVFUNC(0x4114, NvHostAsGpu, Remap)
|
NVFUNC(0x4114, NvHostAsGpu, Remap)
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -16,14 +16,18 @@ namespace skyline::service::nvdrv::device {
|
|||||||
channelFence.UpdateValue(hostSyncpoint);
|
channelFence.UpdateValue(hostSyncpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostChannel::SetNvmapFd(IoctlData &buffer) {}
|
NvStatus NvHostChannel::SetNvmapFd(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
|
return NvStatus::Success;
|
||||||
|
}
|
||||||
|
|
||||||
void NvHostChannel::SetSubmitTimeout(IoctlData &buffer) {}
|
NvStatus NvHostChannel::SetSubmitTimeout(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
|
return NvStatus::Success;
|
||||||
|
}
|
||||||
|
|
||||||
void NvHostChannel::SubmitGpfifo(IoctlData &buffer) {
|
NvStatus NvHostChannel::SubmitGpfifo(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
struct Data {
|
struct Data {
|
||||||
u64 address;
|
u64 address; // In
|
||||||
u32 numEntries;
|
u32 numEntries; // In
|
||||||
union {
|
union {
|
||||||
struct __attribute__((__packed__)) {
|
struct __attribute__((__packed__)) {
|
||||||
bool fenceWait : 1;
|
bool fenceWait : 1;
|
||||||
@ -35,46 +39,50 @@ namespace skyline::service::nvdrv::device {
|
|||||||
bool incrementWithValue : 1;
|
bool incrementWithValue : 1;
|
||||||
};
|
};
|
||||||
u32 raw;
|
u32 raw;
|
||||||
} flags;
|
} flags; // In
|
||||||
Fence fence;
|
Fence fence; // InOut
|
||||||
} &args = state.process->GetReference<Data>(buffer.output.at(0).address);
|
} &data = util::As<Data>(buffer);
|
||||||
|
|
||||||
auto driver = nvdrv::driver.lock();
|
auto driver = nvdrv::driver.lock();
|
||||||
auto &hostSyncpoint = driver->hostSyncpoint;
|
auto &hostSyncpoint = driver->hostSyncpoint;
|
||||||
|
|
||||||
if (args.flags.fenceWait) {
|
if (data.flags.fenceWait) {
|
||||||
if (args.flags.incrementWithValue) {
|
if (data.flags.incrementWithValue)
|
||||||
buffer.status = NvStatus::BadValue;
|
return NvStatus::BadValue;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hostSyncpoint.HasSyncpointExpired(args.fence.id, args.fence.value))
|
if (hostSyncpoint.HasSyncpointExpired(data.fence.id, data.fence.value))
|
||||||
throw exception("Waiting on a fence through SubmitGpfifo is unimplemented");
|
throw exception("Waiting on a fence through SubmitGpfifo is unimplemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
state.gpu->gpfifo.Push(std::span(state.process->GetPointer<gpu::gpfifo::GpEntry>(args.address), args.numEntries));
|
state.gpu->gpfifo.Push(std::span(state.process->GetPointer<gpu::gpfifo::GpEntry>(data.address), data.numEntries));
|
||||||
|
|
||||||
args.fence.id = channelFence.id;
|
data.fence.id = channelFence.id;
|
||||||
|
|
||||||
u32 increment = (args.flags.fenceIncrement ? 2 : 0) + (args.flags.incrementWithValue ? args.fence.value : 0);
|
u32 increment = (data.flags.fenceIncrement ? 2 : 0) + (data.flags.incrementWithValue ? data.fence.value : 0);
|
||||||
args.fence.value = hostSyncpoint.IncrementSyncpointMaxExt(args.fence.id, increment);
|
data.fence.value = hostSyncpoint.IncrementSyncpointMaxExt(data.fence.id, increment);
|
||||||
|
|
||||||
if (args.flags.fenceIncrement)
|
if (data.flags.fenceIncrement)
|
||||||
throw exception("Incrementing a fence through SubmitGpfifo is unimplemented");
|
throw exception("Incrementing a fence through SubmitGpfifo is unimplemented");
|
||||||
|
|
||||||
args.flags.raw = 0;
|
data.flags.raw = 0;
|
||||||
|
|
||||||
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostChannel::AllocObjCtx(IoctlData &buffer) {}
|
NvStatus NvHostChannel::AllocObjCtx(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
|
return NvStatus::Success;
|
||||||
|
}
|
||||||
|
|
||||||
void NvHostChannel::ZcullBind(IoctlData &buffer) {}
|
NvStatus NvHostChannel::ZcullBind(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
|
return NvStatus::Success;
|
||||||
|
}
|
||||||
|
|
||||||
void NvHostChannel::SetErrorNotifier(IoctlData &buffer) {}
|
NvStatus NvHostChannel::SetErrorNotifier(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
|
return NvStatus::Success;
|
||||||
|
}
|
||||||
|
|
||||||
void NvHostChannel::SetPriority(IoctlData &buffer) {
|
NvStatus NvHostChannel::SetPriority(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
auto priority = state.process->GetObject<NvChannelPriority>(buffer.input.at(0).address);
|
switch (util::As<NvChannelPriority>(buffer)) {
|
||||||
|
|
||||||
switch (priority) {
|
|
||||||
case NvChannelPriority::Low:
|
case NvChannelPriority::Low:
|
||||||
timeslice = 1300;
|
timeslice = 1300;
|
||||||
break;
|
break;
|
||||||
@ -85,23 +93,29 @@ namespace skyline::service::nvdrv::device {
|
|||||||
timeslice = 5200;
|
timeslice = 5200;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostChannel::AllocGpfifoEx2(IoctlData &buffer) {
|
NvStatus NvHostChannel::AllocGpfifoEx2(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
struct Data {
|
struct Data {
|
||||||
u32 numEntries;
|
u32 numEntries; // In
|
||||||
u32 numJobs;
|
u32 numJobs; // In
|
||||||
u32 flags;
|
u32 flags; // In
|
||||||
Fence fence;
|
Fence fence; // Out
|
||||||
u32 reserved[3];
|
u32 reserved[3]; // In
|
||||||
} &args = state.process->GetReference<Data>(buffer.input.at(0).address);
|
} &data = util::As<Data>(buffer);
|
||||||
|
|
||||||
auto driver = nvdrv::driver.lock();
|
auto driver = nvdrv::driver.lock();
|
||||||
channelFence.UpdateValue(driver->hostSyncpoint);
|
channelFence.UpdateValue(driver->hostSyncpoint);
|
||||||
args.fence = channelFence;
|
data.fence = channelFence;
|
||||||
|
|
||||||
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostChannel::SetUserData(IoctlData &buffer) {}
|
NvStatus NvHostChannel::SetUserData(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
|
return NvStatus::Success;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<type::KEvent> NvHostChannel::QueryEvent(u32 eventId) {
|
std::shared_ptr<type::KEvent> NvHostChannel::QueryEvent(u32 eventId) {
|
||||||
switch (eventId) {
|
switch (eventId) {
|
||||||
|
@ -30,47 +30,47 @@ namespace skyline::service::nvdrv::device {
|
|||||||
/**
|
/**
|
||||||
* @brief This sets the nvmap file descriptor (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_NVMAP_FD)
|
* @brief This sets the nvmap file descriptor (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_NVMAP_FD)
|
||||||
*/
|
*/
|
||||||
void SetNvmapFd(IoctlData &buffer);
|
NvStatus SetNvmapFd(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This sets the timeout for the channel (https://switchbrew.org/wiki/NV_services#NVHOST_IOCTL_CHANNEL_SET_SUBMIT_TIMEOUT)
|
* @brief This sets the timeout for the channel (https://switchbrew.org/wiki/NV_services#NVHOST_IOCTL_CHANNEL_SET_SUBMIT_TIMEOUT)
|
||||||
*/
|
*/
|
||||||
void SetSubmitTimeout(IoctlData &buffer);
|
NvStatus SetSubmitTimeout(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This submits a command to the GPFIFO (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO)
|
* @brief This submits a command to the GPFIFO (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO)
|
||||||
*/
|
*/
|
||||||
void SubmitGpfifo(IoctlData &buffer);
|
NvStatus SubmitGpfifo(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This allocates a graphic context object (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_ALLOC_OBJ_CTX)
|
* @brief This allocates a graphic context object (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_ALLOC_OBJ_CTX)
|
||||||
*/
|
*/
|
||||||
void AllocObjCtx(IoctlData &buffer);
|
NvStatus AllocObjCtx(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This initializes the error notifier for this channel (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_ZCULL_BIND)
|
* @brief This initializes the error notifier for this channel (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_ZCULL_BIND)
|
||||||
*/
|
*/
|
||||||
void ZcullBind(IoctlData &buffer);
|
NvStatus ZcullBind(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This initializes the error notifier for this channel (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_ERROR_NOTIFIER)
|
* @brief This initializes the error notifier for this channel (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_ERROR_NOTIFIER)
|
||||||
*/
|
*/
|
||||||
void SetErrorNotifier(IoctlData &buffer);
|
NvStatus SetErrorNotifier(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This sets the priority of the channel (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_PRIORITY)
|
* @brief This sets the priority of the channel (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_PRIORITY)
|
||||||
*/
|
*/
|
||||||
void SetPriority(IoctlData &buffer);
|
NvStatus SetPriority(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This allocates a GPFIFO entry (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_ALLOC_GPFIFO_EX2)
|
* @brief This allocates a GPFIFO entry (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_ALLOC_GPFIFO_EX2)
|
||||||
*/
|
*/
|
||||||
void AllocGpfifoEx2(IoctlData &buffer);
|
NvStatus AllocGpfifoEx2(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This sets the user specific data (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_USER_DATA)
|
* @brief This sets the user specific data (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_USER_DATA)
|
||||||
*/
|
*/
|
||||||
void SetUserData(IoctlData &buffer);
|
NvStatus SetUserData(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
std::shared_ptr<type::KEvent> QueryEvent(u32 eventId);
|
std::shared_ptr<type::KEvent> QueryEvent(u32 eventId);
|
||||||
|
|
||||||
|
@ -72,91 +72,79 @@ namespace skyline::service::nvdrv::device {
|
|||||||
throw exception("Failed to find a free nvhost event!");
|
throw exception("Failed to find a free nvhost event!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostCtrl::EventWaitImpl(IoctlData &buffer, bool async) {
|
NvStatus NvHostCtrl::EventWaitImpl(std::span<u8> buffer, bool async) {
|
||||||
struct Data {
|
struct Data {
|
||||||
Fence fence;
|
Fence fence; // In
|
||||||
u32 timeout;
|
u32 timeout; // In
|
||||||
EventValue value;
|
EventValue value; // InOut
|
||||||
} &args = state.process->GetReference<Data>(buffer.output.at(0).address);
|
} &data = util::As<Data>(buffer);
|
||||||
|
|
||||||
if (args.fence.id >= constant::MaxHwSyncpointCount) {
|
if (data.fence.id >= constant::MaxHwSyncpointCount)
|
||||||
buffer.status = NvStatus::BadValue;
|
return NvStatus::BadValue;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.timeout == 0) {
|
if (data.timeout == 0)
|
||||||
buffer.status = NvStatus::Timeout;
|
return NvStatus::Timeout;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto driver = nvdrv::driver.lock();
|
auto driver = nvdrv::driver.lock();
|
||||||
auto &hostSyncpoint = driver->hostSyncpoint;
|
auto &hostSyncpoint = driver->hostSyncpoint;
|
||||||
|
|
||||||
// Check if the syncpoint has already expired using the last known values
|
// Check if the syncpoint has already expired using the last known values
|
||||||
if (hostSyncpoint.HasSyncpointExpired(args.fence.id, args.fence.value)) {
|
if (hostSyncpoint.HasSyncpointExpired(data.fence.id, data.fence.value)) {
|
||||||
args.value.val = hostSyncpoint.ReadSyncpointMinValue(args.fence.id);
|
data.value.val = hostSyncpoint.ReadSyncpointMinValue(data.fence.id);
|
||||||
return;
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync the syncpoint with the GPU then check again
|
// Sync the syncpoint with the GPU then check again
|
||||||
auto minVal = hostSyncpoint.UpdateMin(args.fence.id);
|
auto minVal = hostSyncpoint.UpdateMin(data.fence.id);
|
||||||
if (hostSyncpoint.HasSyncpointExpired(args.fence.id, args.fence.value)) {
|
if (hostSyncpoint.HasSyncpointExpired(data.fence.id, data.fence.value)) {
|
||||||
args.value.val = minVal;
|
data.value.val = minVal;
|
||||||
return;
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 userEventId{};
|
u32 userEventId{};
|
||||||
|
if (async) {
|
||||||
|
if (data.value.val >= constant::NvHostEventCount || !events.at(data.value.val))
|
||||||
|
return NvStatus::BadValue;
|
||||||
|
|
||||||
|
userEventId = data.value.val;
|
||||||
|
} else {
|
||||||
|
data.fence.value = 0;
|
||||||
|
|
||||||
|
userEventId = FindFreeEvent(data.fence.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& event = *events.at(userEventId);
|
||||||
|
if (event.state == NvHostEvent::State::Cancelled || event.state == NvHostEvent::State::Available || event.state == NvHostEvent::State::Signaled) {
|
||||||
|
state.logger->Debug("Now waiting on nvhost event: {} with fence: {}", userEventId, data.fence.id);
|
||||||
|
event.Wait(state.gpu, data.fence);
|
||||||
|
|
||||||
|
data.value.val = 0;
|
||||||
|
|
||||||
if (async) {
|
if (async) {
|
||||||
if (args.value.val >= constant::NvHostEventCount || !events.at(args.value.val)) {
|
data.value.syncpointIdAsync = data.fence.id;
|
||||||
buffer.status = NvStatus::BadValue;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
userEventId = args.value.val;
|
|
||||||
} else {
|
} else {
|
||||||
args.fence.value = 0;
|
data.value.syncpointIdNonAsync = data.fence.id;
|
||||||
|
data.value.nonAsync = true;
|
||||||
userEventId = FindFreeEvent(args.fence.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto event = &*events.at(userEventId);
|
data.value.val |= userEventId;
|
||||||
|
|
||||||
if (event->state == NvHostEvent::State::Cancelled || event->state == NvHostEvent::State::Available || event->state == NvHostEvent::State::Signaled) {
|
return NvStatus::Timeout;
|
||||||
state.logger->Debug("Now waiting on nvhost event: {} with fence: {}", userEventId, args.fence.id);
|
|
||||||
event->Wait(state.gpu, args.fence);
|
|
||||||
|
|
||||||
args.value.val = 0;
|
|
||||||
|
|
||||||
if (async) {
|
|
||||||
args.value.syncpointIdAsync = args.fence.id;
|
|
||||||
} else {
|
} else {
|
||||||
args.value.syncpointIdNonAsync = args.fence.id;
|
return NvStatus::BadValue;
|
||||||
args.value.nonAsync = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
args.value.val |= userEventId;
|
|
||||||
|
|
||||||
buffer.status = NvStatus::Timeout;
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
buffer.status = NvStatus::BadValue;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostCtrl::GetConfig(IoctlData &buffer) {
|
NvStatus NvHostCtrl::GetConfig(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
buffer.status = NvStatus::BadValue;
|
return NvStatus::BadValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostCtrl::EventSignal(IoctlData &buffer) {
|
NvStatus NvHostCtrl::EventSignal(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
auto userEventId = static_cast<u16>(state.process->GetObject<u32>(buffer.input.at(0).address));
|
auto userEventId{util::As<u32>(buffer)};
|
||||||
state.logger->Debug("Signalling nvhost event: {}", userEventId);
|
state.logger->Debug("Signalling nvhost event: {}", userEventId);
|
||||||
|
|
||||||
if (userEventId >= constant::NvHostEventCount || !events.at(userEventId)) {
|
if (userEventId >= constant::NvHostEventCount || !events.at(userEventId))
|
||||||
buffer.status = NvStatus::BadValue;
|
return NvStatus::BadValue;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto &event = *events.at(userEventId);
|
auto &event = *events.at(userEventId);
|
||||||
|
|
||||||
@ -171,30 +159,32 @@ namespace skyline::service::nvdrv::device {
|
|||||||
auto driver = nvdrv::driver.lock();
|
auto driver = nvdrv::driver.lock();
|
||||||
auto &hostSyncpoint = driver->hostSyncpoint;
|
auto &hostSyncpoint = driver->hostSyncpoint;
|
||||||
hostSyncpoint.UpdateMin(event.fence.id);
|
hostSyncpoint.UpdateMin(event.fence.id);
|
||||||
|
|
||||||
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostCtrl::EventWait(IoctlData &buffer) {
|
NvStatus NvHostCtrl::EventWait(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
EventWaitImpl(buffer, false);
|
return EventWaitImpl(buffer, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostCtrl::EventWaitAsync(IoctlData &buffer) {
|
NvStatus NvHostCtrl::EventWaitAsync(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
EventWaitImpl(buffer, true);
|
return EventWaitImpl(buffer, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostCtrl::EventRegister(IoctlData &buffer) {
|
NvStatus NvHostCtrl::EventRegister(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
auto userEventId = state.process->GetObject<u32>(buffer.input.at(0).address);
|
auto userEventId{util::As<u32>(buffer)};
|
||||||
state.logger->Debug("Registering nvhost event: {}", userEventId);
|
state.logger->Debug("Registering nvhost event: {}", userEventId);
|
||||||
|
|
||||||
auto &event = events.at(userEventId);
|
auto &event = events.at(userEventId);
|
||||||
|
|
||||||
if (event)
|
if (event)
|
||||||
throw exception("Recreating events is unimplemented");
|
throw exception("Recreating events is unimplemented");
|
||||||
|
|
||||||
event = NvHostEvent(state);
|
event = NvHostEvent(state);
|
||||||
|
|
||||||
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<type::KEvent> NvHostCtrl::QueryEvent(u32 eventId) {
|
std::shared_ptr<type::KEvent> NvHostCtrl::QueryEvent(u32 eventId) {
|
||||||
auto eventValue = EventValue{.val = eventId};
|
EventValue eventValue{.val = eventId};
|
||||||
const auto &event = events.at(eventValue.nonAsync ? eventValue.eventSlotNonAsync : eventValue.eventSlotAsync);
|
const auto &event = events.at(eventValue.nonAsync ? eventValue.eventSlotNonAsync : eventValue.eventSlotAsync);
|
||||||
|
|
||||||
if (event && event->fence.id == (eventValue.nonAsync ? eventValue.syncpointIdNonAsync : eventValue.syncpointIdAsync))
|
if (event && event->fence.id == (eventValue.nonAsync ? eventValue.syncpointIdNonAsync : eventValue.syncpointIdAsync))
|
||||||
|
@ -87,7 +87,7 @@ namespace skyline {
|
|||||||
*/
|
*/
|
||||||
u32 FindFreeEvent(u32 syncpointId);
|
u32 FindFreeEvent(u32 syncpointId);
|
||||||
|
|
||||||
void EventWaitImpl(IoctlData &buffer, bool async);
|
NvStatus EventWaitImpl(std::span<u8> buffer, bool async);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NvHostCtrl(const DeviceState &state);
|
NvHostCtrl(const DeviceState &state);
|
||||||
@ -95,27 +95,27 @@ namespace skyline {
|
|||||||
/**
|
/**
|
||||||
* @brief This gets the value of an nvdrv setting, it returns an error code on production switches (https://switchbrew.org/wiki/NV_services#NVHOST_IOCTL_CTRL_GET_CONFIG)
|
* @brief This gets the value of an nvdrv setting, it returns an error code on production switches (https://switchbrew.org/wiki/NV_services#NVHOST_IOCTL_CTRL_GET_CONFIG)
|
||||||
*/
|
*/
|
||||||
void GetConfig(IoctlData &buffer);
|
NvStatus GetConfig(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This signals an NvHost event (https://switchbrew.org/wiki/NV_services#NVHOST_IOCTL_CTRL_EVENT_SIGNAL)
|
* @brief This signals an NvHost event (https://switchbrew.org/wiki/NV_services#NVHOST_IOCTL_CTRL_EVENT_SIGNAL)
|
||||||
*/
|
*/
|
||||||
void EventSignal(IoctlData &buffer);
|
NvStatus EventSignal(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This synchronously waits on an NvHost event (https://switchbrew.org/wiki/NV_services#NVHOST_IOCTL_CTRL_EVENT_WAIT)
|
* @brief This synchronously waits on an NvHost event (https://switchbrew.org/wiki/NV_services#NVHOST_IOCTL_CTRL_EVENT_WAIT)
|
||||||
*/
|
*/
|
||||||
void EventWait(IoctlData &buffer);
|
NvStatus EventWait(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This asynchronously waits on an NvHost event (https://switchbrew.org/wiki/NV_services#NVHOST_IOCTL_CTRL_EVENT_WAIT_ASYNC)
|
* @brief This asynchronously waits on an NvHost event (https://switchbrew.org/wiki/NV_services#NVHOST_IOCTL_CTRL_EVENT_WAIT_ASYNC)
|
||||||
*/
|
*/
|
||||||
void EventWaitAsync(IoctlData &buffer);
|
NvStatus EventWaitAsync(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This registers an NvHost event (https://switchbrew.org/wiki/NV_services#NVHOST_IOCTL_CTRL_EVENT_REGISTER)
|
* @brief This registers an NvHost event (https://switchbrew.org/wiki/NV_services#NVHOST_IOCTL_CTRL_EVENT_REGISTER)
|
||||||
*/
|
*/
|
||||||
void EventRegister(IoctlData &buffer);
|
NvStatus EventRegister(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
std::shared_ptr<type::KEvent> QueryEvent(u32 eventId);
|
std::shared_ptr<type::KEvent> QueryEvent(u32 eventId);
|
||||||
|
|
||||||
|
@ -7,13 +7,13 @@
|
|||||||
namespace skyline::service::nvdrv::device {
|
namespace skyline::service::nvdrv::device {
|
||||||
NvHostCtrlGpu::NvHostCtrlGpu(const DeviceState &state) : errorNotifierEvent(std::make_shared<type::KEvent>(state)), unknownEvent(std::make_shared<type::KEvent>(state)), NvDevice(state) {}
|
NvHostCtrlGpu::NvHostCtrlGpu(const DeviceState &state) : errorNotifierEvent(std::make_shared<type::KEvent>(state)), unknownEvent(std::make_shared<type::KEvent>(state)), NvDevice(state) {}
|
||||||
|
|
||||||
void NvHostCtrlGpu::ZCullGetCtxSize(IoctlData &buffer) {
|
NvStatus NvHostCtrlGpu::ZCullGetCtxSize(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
u32 size = 0x1;
|
util::As<u32>(buffer) = 0x1;
|
||||||
state.process->WriteMemory(size, buffer.output[0].address);
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostCtrlGpu::ZCullGetInfo(IoctlData &buffer) {
|
NvStatus NvHostCtrlGpu::ZCullGetInfo(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
struct {
|
struct ZCullInfo {
|
||||||
u32 widthAlignPixels{0x20};
|
u32 widthAlignPixels{0x20};
|
||||||
u32 heightAlignPixels{0x20};
|
u32 heightAlignPixels{0x20};
|
||||||
u32 pixelSquaresByAliquots{0x400};
|
u32 pixelSquaresByAliquots{0x400};
|
||||||
@ -26,119 +26,84 @@ namespace skyline::service::nvdrv::device {
|
|||||||
u32 subregionCount{0x10};
|
u32 subregionCount{0x10};
|
||||||
} zCullInfo;
|
} zCullInfo;
|
||||||
|
|
||||||
state.process->WriteMemory(zCullInfo, buffer.output[0].address);
|
util::As<ZCullInfo>(buffer) = zCullInfo;
|
||||||
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostCtrlGpu::GetCharacteristics(IoctlData &buffer) {
|
NvStatus NvHostCtrlGpu::GetCharacteristics(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
struct GpuCharacteristics {
|
struct GpuCharacteristics {
|
||||||
u32 arch; // 0x120 (NVGPU_GPU_ARCH_GM200)
|
u32 arch{0x120}; // NVGPU_GPU_ARCH_GM200
|
||||||
u32 impl; // 0xB (NVGPU_GPU_IMPL_GM20B) or 0xE (NVGPU_GPU_IMPL_GM20B_B)
|
u32 impl{0xB}; // 0xB (NVGPU_GPU_IMPL_GM20B) or 0xE (NVGPU_GPU_IMPL_GM20B_B)
|
||||||
u32 rev; // 0xA1 (Revision A1)
|
u32 rev{0xA1};
|
||||||
u32 numGpc; // 0x1
|
u32 numGpc{0x1};
|
||||||
u64 l2CacheSize; // 0x40000
|
u64 l2CacheSize{0x40000};
|
||||||
u64 onBoardVideoMemorySize; // 0x0 (not used)
|
u64 onBoardVideoMemorySize{}; // UMA
|
||||||
u32 numTpcPerGpc; // 0x2
|
u32 numTpcPerGpc{0x2};
|
||||||
u32 busType; // 0x20 (NVGPU_GPU_BUS_TYPE_AXI)
|
u32 busType{0x20}; // NVGPU_GPU_BUS_TYPE_AXI
|
||||||
u32 bigPageSize; // 0x20000
|
u32 bigPageSize{0x20000};
|
||||||
u32 compressionPageSize; // 0x20000
|
u32 compressionPageSize{0x20000};
|
||||||
u32 pdeCoverageBitCount; // 0x1B
|
u32 pdeCoverageBitCount{0x1B};
|
||||||
u32 availableBigPageSizes; // 0x30000
|
u32 availableBigPageSizes{0x30000};
|
||||||
u32 gpcMask; // 0x1
|
u32 gpcMask{0x1};
|
||||||
u32 smArchSmVersion; // 0x503 (Maxwell Generation 5.0.3)
|
u32 smArchSmVersion{0x503}; // Maxwell Generation 5.0.3
|
||||||
u32 smArchSpaVersion; // 0x503 (Maxwell Generation 5.0.3)
|
u32 smArchSpaVersion{0x503}; // Maxwell Generation 5.0.3
|
||||||
u32 smArchWarpCount; // 0x80
|
u32 smArchWarpCount{0x80};
|
||||||
u32 gpuVaBitCount; // 0x28
|
u32 gpuVaBitCount{0x28};
|
||||||
u32 reserved; // NULL
|
u32 reserved{};
|
||||||
u64 flags; // 0x55 (HAS_SYNCPOINTS | SUPPORT_SPARSE_ALLOCS | SUPPORT_CYCLE_STATS | SUPPORT_CYCLE_STATS_SNAPSHOT)
|
u64 flags{0x55}; // HAS_SYNCPOINTS | SUPPORT_SPARSE_ALLOCS | SUPPORT_CYCLE_STATS | SUPPORT_CYCLE_STATS_SNAPSHOT
|
||||||
u32 twodClass; // 0x902D (FERMI_TWOD_A)
|
u32 twodClass{0x902D}; // FERMI_TWOD_A
|
||||||
u32 threedClass; // 0xB197 (MAXWELL_B)
|
u32 threedClass{0xB197}; // MAXWELL_B
|
||||||
u32 computeClass; // 0xB1C0 (MAXWELL_COMPUTE_B)
|
u32 computeClass{0xB1C0}; // MAXWELL_COMPUTE_B
|
||||||
u32 gpfifoClass; // 0xB06F (MAXWELL_CHANNEL_GPFIFO_A)
|
u32 gpfifoClass{0xB06F}; // MAXWELL_CHANNEL_GPFIFO_A
|
||||||
u32 inlineToMemoryClass; // 0xA140 (KEPLER_INLINE_TO_MEMORY_B)
|
u32 inlineToMemoryClass{0xA140}; // KEPLER_INLINE_TO_MEMORY_B
|
||||||
u32 dmaCopyClass; // 0xB0B5 (MAXWELL_DMA_COPY_A)
|
u32 dmaCopyClass{0xA140}; // MAXWELL_DMA_COPY_A
|
||||||
u32 maxFbpsCount; // 0x1
|
u32 maxFbpsCount{0x1}; // 0x1
|
||||||
u32 fbpEnMask; // 0x0 (disabled)
|
u32 fbpEnMask{}; // Disabled
|
||||||
u32 maxLtcPerFbp; // 0x2
|
u32 maxLtcPerFbp{0x2};
|
||||||
u32 maxLtsPerLtc; // 0x1
|
u32 maxLtsPerLtc{0x1};
|
||||||
u32 maxTexPerTpc; // 0x0 (not supported)
|
u32 maxTexPerTpc{}; // Not Supported
|
||||||
u32 maxGpcCount; // 0x1
|
u32 maxGpcCount{0x1};
|
||||||
u32 ropL2EnMask0; // 0x21D70 (fuse_status_opt_rop_l2_fbp_r)
|
u32 ropL2EnMask0{0x21D70}; // fuse_status_opt_rop_l2_fbp_r
|
||||||
u32 ropL2EnMask1; // 0x0
|
u32 ropL2EnMask1{};
|
||||||
u64 chipName; // 0x6230326D67 ("gm20b")
|
u64 chipName{util::MakeMagic<u64>("gm20b")};
|
||||||
u64 grCompbitStoreBaseHw; // 0x0 (not supported)
|
u64 grCompbitStoreBaseHw{}; // Not Supported
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Data {
|
struct Data {
|
||||||
u64 gpuCharacteristicsBufSize; // InOut
|
u64 gpuCharacteristicsBufSize; // InOut
|
||||||
u64 gpuCharacteristicsBufAddr; // In
|
u64 gpuCharacteristicsBufAddr; // In
|
||||||
GpuCharacteristics gpuCharacteristics; // Out
|
GpuCharacteristics gpuCharacteristics; // Out
|
||||||
} data = state.process->GetObject<Data>(buffer.input[0].address);
|
} &data = util::As<Data>(buffer);
|
||||||
|
|
||||||
data.gpuCharacteristics = {
|
if (data.gpuCharacteristicsBufSize < sizeof(GpuCharacteristics))
|
||||||
.arch = 0x120,
|
return NvStatus::InvalidSize;
|
||||||
.impl = 0xB,
|
|
||||||
.rev = 0xA1,
|
|
||||||
.numGpc = 0x1,
|
|
||||||
.l2CacheSize = 0x40000,
|
|
||||||
.onBoardVideoMemorySize = 0x0,
|
|
||||||
.numTpcPerGpc = 0x2,
|
|
||||||
.busType = 0x20,
|
|
||||||
.bigPageSize = 0x20000,
|
|
||||||
.compressionPageSize = 0x20000,
|
|
||||||
.pdeCoverageBitCount = 0x1B,
|
|
||||||
.availableBigPageSizes = 0x30000,
|
|
||||||
.gpcMask = 0x1,
|
|
||||||
.smArchSmVersion = 0x503,
|
|
||||||
.smArchSpaVersion = 0x503,
|
|
||||||
.smArchWarpCount = 0x80,
|
|
||||||
.gpuVaBitCount = 0x2,
|
|
||||||
.flags = 0x55,
|
|
||||||
.twodClass = 0x902D,
|
|
||||||
.threedClass = 0xB197,
|
|
||||||
.computeClass = 0xB1C0,
|
|
||||||
.gpfifoClass = 0xB06F,
|
|
||||||
.inlineToMemoryClass = 0xA140,
|
|
||||||
.dmaCopyClass = 0xB0B5,
|
|
||||||
.maxFbpsCount = 0x1,
|
|
||||||
.fbpEnMask = 0x0,
|
|
||||||
.maxLtcPerFbp = 0x2,
|
|
||||||
.maxLtsPerLtc = 0x1,
|
|
||||||
.maxTexPerTpc = 0x0,
|
|
||||||
.maxGpcCount = 0x1,
|
|
||||||
.ropL2EnMask0 = 0x21D70,
|
|
||||||
.ropL2EnMask1 = 0x0,
|
|
||||||
.chipName = 0x6230326D67,
|
|
||||||
.grCompbitStoreBaseHw = 0x0
|
|
||||||
};
|
|
||||||
|
|
||||||
data.gpuCharacteristicsBufSize = 0xA0;
|
data.gpuCharacteristics = GpuCharacteristics{};
|
||||||
|
data.gpuCharacteristicsBufSize = sizeof(GpuCharacteristics);
|
||||||
|
|
||||||
state.process->WriteMemory(data, buffer.output[0].address);
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostCtrlGpu::GetTpcMasks(IoctlData &buffer) {
|
NvStatus NvHostCtrlGpu::GetTpcMasks(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
struct Data {
|
struct Data {
|
||||||
u32 maskBufSize; // In
|
u32 maskBufSize; // In
|
||||||
u32 reserved[3]; // In
|
u32 reserved[3]; // In
|
||||||
u64 maskBuf; // Out
|
u64 maskBuf; // Out
|
||||||
} data = state.process->GetObject<Data>(buffer.input[0].address);
|
} &data = util::As<Data>(buffer);
|
||||||
|
|
||||||
if (data.maskBufSize)
|
if (data.maskBufSize)
|
||||||
data.maskBuf = 0x3;
|
data.maskBuf = 0x3;
|
||||||
|
|
||||||
state.process->WriteMemory(data, buffer.output[0].address);
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostCtrlGpu::GetActiveSlotMask(IoctlData &buffer) {
|
NvStatus NvHostCtrlGpu::GetActiveSlotMask(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
struct Data {
|
struct Data {
|
||||||
u32 slot; // Out
|
u32 slot{0x07}; // Out
|
||||||
u32 mask; // Out
|
u32 mask{0x01}; // Out
|
||||||
} data = {
|
} data;
|
||||||
.slot = 0x07,
|
util::As<Data>(buffer) = data;
|
||||||
.mask = 0x01
|
return NvStatus::Success;
|
||||||
};
|
|
||||||
|
|
||||||
state.process->WriteMemory(data, buffer.output[0].address);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<type::KEvent> NvHostCtrlGpu::QueryEvent(u32 eventId) {
|
std::shared_ptr<type::KEvent> NvHostCtrlGpu::QueryEvent(u32 eventId) {
|
||||||
@ -151,5 +116,4 @@ namespace skyline::service::nvdrv::device {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,27 +20,27 @@ namespace skyline::service::nvdrv::device {
|
|||||||
/**
|
/**
|
||||||
* @brief This returns a u32 GPU ZCULL Context Size (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_ZCULL_GET_CTX_SIZE)
|
* @brief This returns a u32 GPU ZCULL Context Size (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_ZCULL_GET_CTX_SIZE)
|
||||||
*/
|
*/
|
||||||
void ZCullGetCtxSize(IoctlData &buffer);
|
NvStatus ZCullGetCtxSize(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This returns a the GPU ZCULL Information (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_ZCULL_GET_INFO)
|
* @brief This returns a the GPU ZCULL Information (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_ZCULL_GET_INFO)
|
||||||
*/
|
*/
|
||||||
void ZCullGetInfo(IoctlData &buffer);
|
NvStatus ZCullGetInfo(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This returns a struct with certain GPU characteristics (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_GET_CHARACTERISTICS)
|
* @brief This returns a struct with certain GPU characteristics (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_GET_CHARACTERISTICS)
|
||||||
*/
|
*/
|
||||||
void GetCharacteristics(IoctlData &buffer);
|
NvStatus GetCharacteristics(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This returns the TPC mask value for each GPC (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_GET_TPC_MASKS)
|
* @brief This returns the TPC mask value for each GPC (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_GET_TPC_MASKS)
|
||||||
*/
|
*/
|
||||||
void GetTpcMasks(IoctlData &buffer);
|
NvStatus GetTpcMasks(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This returns the mask value for a ZBC slot (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_ZBC_GET_ACTIVE_SLOT_MASK)
|
* @brief This returns the mask value for a ZBC slot (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_ZBC_GET_ACTIVE_SLOT_MASK)
|
||||||
*/
|
*/
|
||||||
void GetActiveSlotMask(IoctlData &buffer);
|
NvStatus GetActiveSlotMask(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
std::shared_ptr<type::KEvent> QueryEvent(u32 eventId);
|
std::shared_ptr<type::KEvent> QueryEvent(u32 eventId);
|
||||||
|
|
||||||
|
@ -9,43 +9,38 @@ namespace skyline::service::nvdrv::device {
|
|||||||
|
|
||||||
NvMap::NvMap(const DeviceState &state) : NvDevice(state) {}
|
NvMap::NvMap(const DeviceState &state) : NvDevice(state) {}
|
||||||
|
|
||||||
void NvMap::Create(IoctlData &buffer) {
|
NvStatus NvMap::Create(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
struct Data {
|
struct Data {
|
||||||
u32 size; // In
|
u32 size; // In
|
||||||
u32 handle; // Out
|
u32 handle; // Out
|
||||||
} data = state.process->GetObject<Data>(buffer.input[0].address);
|
} &data = util::As<Data>(buffer);
|
||||||
|
|
||||||
handleTable[handleIndex] = std::make_shared<NvMapObject>(idIndex++, data.size);
|
handleTable[handleIndex] = std::make_shared<NvMapObject>(idIndex++, data.size);
|
||||||
data.handle = handleIndex++;
|
data.handle = handleIndex++;
|
||||||
|
|
||||||
state.process->WriteMemory(data, buffer.output[0].address);
|
state.logger->Debug("Size: 0x{:X} -> Handle: 0x{:X}", data.size, data.handle);
|
||||||
state.logger->Debug("Create: Input: Size: 0x{:X}, Output: Handle: 0x{:X}, Status: {}", data.size, data.handle, buffer.status);
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvMap::FromId(IoctlData &buffer) {
|
NvStatus NvMap::FromId(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
struct Data {
|
struct Data {
|
||||||
u32 id; // In
|
u32 id; // In
|
||||||
u32 handle; // Out
|
u32 handle; // Out
|
||||||
} data = state.process->GetObject<Data>(buffer.input[0].address);
|
} &data = util::As<Data>(buffer);
|
||||||
|
|
||||||
bool found{};
|
|
||||||
for (const auto &object : handleTable) {
|
for (const auto &object : handleTable) {
|
||||||
if (object.second->id == data.id) {
|
if (object.second->id == data.id) {
|
||||||
data.handle = object.first;
|
data.handle = object.first;
|
||||||
found = true;
|
state.logger->Debug("ID: 0x{:X} -> Handle: 0x{:X}", data.id, data.handle);
|
||||||
break;
|
return NvStatus::Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (found)
|
state.logger->Warn("Handle not found for ID: 0x{:X}", data.id);
|
||||||
state.process->WriteMemory(data, buffer.output[0].address);
|
return NvStatus::BadValue;
|
||||||
else
|
|
||||||
buffer.status = NvStatus::BadValue;
|
|
||||||
|
|
||||||
state.logger->Debug("FromId: Input: Handle: 0x{:X}, Output: ID: 0x{:X}, Status: {}", data.handle, data.id, buffer.status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvMap::Alloc(IoctlData &buffer) {
|
NvStatus NvMap::Alloc(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
struct Data {
|
struct Data {
|
||||||
u32 handle; // In
|
u32 handle; // In
|
||||||
u32 heapMask; // In
|
u32 heapMask; // In
|
||||||
@ -54,8 +49,9 @@ namespace skyline::service::nvdrv::device {
|
|||||||
u8 kind; // In
|
u8 kind; // In
|
||||||
u8 _pad0_[7];
|
u8 _pad0_[7];
|
||||||
u64 address; // InOut
|
u64 address; // InOut
|
||||||
} data = state.process->GetObject<Data>(buffer.input[0].address);
|
} &data = util::As<Data>(buffer);
|
||||||
|
|
||||||
|
try {
|
||||||
auto &object = handleTable.at(data.handle);
|
auto &object = handleTable.at(data.handle);
|
||||||
object->heapMask = data.heapMask;
|
object->heapMask = data.heapMask;
|
||||||
object->flags = data.flags;
|
object->flags = data.flags;
|
||||||
@ -64,18 +60,24 @@ namespace skyline::service::nvdrv::device {
|
|||||||
object->address = data.address;
|
object->address = data.address;
|
||||||
object->status = NvMapObject::Status::Allocated;
|
object->status = NvMapObject::Status::Allocated;
|
||||||
|
|
||||||
state.logger->Debug("Alloc: Input: Handle: 0x{:X}, HeapMask: 0x{:X}, Flags: {}, Align: 0x{:X}, Kind: {}, Address: 0x{:X}, Output: Status: {}", data.handle, data.heapMask, data.flags, data.align, data.kind, data.address, buffer.status);
|
state.logger->Debug("Handle: 0x{:X}, HeapMask: 0x{:X}, Flags: {}, Align: 0x{:X}, Kind: {}, Address: 0x{:X}", data.handle, data.heapMask, data.flags, data.align, data.kind, data.address);
|
||||||
|
return NvStatus::Success;
|
||||||
|
} catch (const std::out_of_range &) {
|
||||||
|
state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.handle);
|
||||||
|
return NvStatus::BadParameter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvMap::Free(IoctlData &buffer) {
|
NvStatus NvMap::Free(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
struct Data {
|
struct Data {
|
||||||
u32 handle; // In
|
u32 handle; // In
|
||||||
u32 _pad0_;
|
u32 _pad0_;
|
||||||
u64 address; // Out
|
u64 address; // Out
|
||||||
u32 size; // Out
|
u32 size; // Out
|
||||||
u32 flags; // Out
|
u32 flags; // Out
|
||||||
} data = state.process->GetObject<Data>(buffer.input[0].address);
|
} &data = util::As<Data>(buffer);
|
||||||
|
|
||||||
|
try {
|
||||||
const auto &object = handleTable.at(data.handle);
|
const auto &object = handleTable.at(data.handle);
|
||||||
if (object.use_count() > 1) {
|
if (object.use_count() > 1) {
|
||||||
data.address = static_cast<u32>(object->address);
|
data.address = static_cast<u32>(object->address);
|
||||||
@ -88,59 +90,72 @@ namespace skyline::service::nvdrv::device {
|
|||||||
data.size = object->size;
|
data.size = object->size;
|
||||||
handleTable.erase(data.handle);
|
handleTable.erase(data.handle);
|
||||||
|
|
||||||
state.process->WriteMemory(data, buffer.output[0].address);
|
state.logger->Debug("Handle: 0x{:X} -> Address: 0x{:X}, Size: 0x{:X}, Flags: 0x{:X}", data.handle, data.address, data.size, data.flags);
|
||||||
|
return NvStatus::Success;
|
||||||
|
} catch (const std::out_of_range &) {
|
||||||
|
state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.handle);
|
||||||
|
return NvStatus::BadParameter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvMap::Param(IoctlData &buffer) {
|
NvStatus NvMap::Param(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
enum class Parameter : u32 { Size = 1, Alignment = 2, Base = 3, HeapMask = 4, Kind = 5, Compr = 6 }; // https://android.googlesource.com/kernel/tegra/+/refs/heads/android-tegra-flounder-3.10-marshmallow/include/linux/nvmap.h#102
|
enum class Parameter : u32 { Size = 1, Alignment = 2, Base = 3, HeapMask = 4, Kind = 5, Compr = 6 }; // https://android.googlesource.com/kernel/tegra/+/refs/heads/android-tegra-flounder-3.10-marshmallow/include/linux/nvmap.h#102
|
||||||
struct Data {
|
struct Data {
|
||||||
u32 handle; // In
|
u32 handle; // In
|
||||||
Parameter parameter; // In
|
Parameter parameter; // In
|
||||||
u32 result; // Out
|
u32 result; // Out
|
||||||
} data = state.process->GetObject<Data>(buffer.input[0].address);
|
} &data = util::As<Data>(buffer);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto &object = handleTable.at(data.handle);
|
auto &object = handleTable.at(data.handle);
|
||||||
|
|
||||||
|
|
||||||
switch (data.parameter) {
|
switch (data.parameter) {
|
||||||
case Parameter::Size:
|
case Parameter::Size:
|
||||||
data.result = object->size;
|
data.result = object->size;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Parameter::Alignment:
|
case Parameter::Alignment:
|
||||||
data.result = object->align;
|
data.result = object->align;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Parameter::HeapMask:
|
case Parameter::HeapMask:
|
||||||
data.result = object->heapMask;
|
data.result = object->heapMask;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Parameter::Kind:
|
case Parameter::Kind:
|
||||||
data.result = object->kind;
|
data.result = object->kind;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Parameter::Compr:
|
case Parameter::Compr:
|
||||||
data.result = 0;
|
data.result = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
buffer.status = NvStatus::NotImplemented;
|
state.logger->Warn("Parameter not implemented: 0x{:X}", data.parameter);
|
||||||
return;
|
return NvStatus::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.process->WriteMemory(data, buffer.output[0].address);
|
state.logger->Debug("Handle: 0x{:X}, Parameter: {} -> Result: 0x{:X}", data.handle, data.parameter, data.result);
|
||||||
state.logger->Debug("Param: Input: Handle: 0x{:X}, Parameter: {}, Output: Result: 0x{:X}, Status: {}", data.handle, data.parameter, data.result, buffer.status);
|
return NvStatus::Success;
|
||||||
} catch (std::exception &e) {
|
} catch (const std::out_of_range &) {
|
||||||
buffer.status = NvStatus::BadParameter;
|
state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.handle);
|
||||||
return;
|
return NvStatus::BadParameter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvMap::GetId(IoctlData &buffer) {
|
NvStatus NvMap::GetId(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
|
||||||
struct Data {
|
struct Data {
|
||||||
u32 id; // Out
|
u32 id; // Out
|
||||||
u32 handle; // In
|
u32 handle; // In
|
||||||
} data = state.process->GetObject<Data>(buffer.input[0].address);
|
} &data = util::As<Data>(buffer);
|
||||||
|
|
||||||
|
try {
|
||||||
data.id = handleTable.at(data.handle)->id;
|
data.id = handleTable.at(data.handle)->id;
|
||||||
|
state.logger->Debug("Handle: 0x{:X} -> ID: 0x{:X}", data.handle, data.id);
|
||||||
state.process->WriteMemory(data, buffer.output[0].address);
|
return NvStatus::Success;
|
||||||
state.logger->Debug("GetId: Input: Handle: 0x{:X}, Output: ID: 0x{:X}, Status: {}", data.handle, data.id, buffer.status);
|
} catch (const std::out_of_range &) {
|
||||||
|
state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.handle);
|
||||||
|
return NvStatus::BadParameter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,32 +44,32 @@ namespace skyline::service::nvdrv::device {
|
|||||||
/**
|
/**
|
||||||
* @brief This creates an NvMapObject and returns an handle to it (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_CREATE)
|
* @brief This creates an NvMapObject and returns an handle to it (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_CREATE)
|
||||||
*/
|
*/
|
||||||
void Create(IoctlData &buffer);
|
NvStatus Create(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This returns the handle of an NvMapObject from it's ID (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_FROM_ID)
|
* @brief This returns the handle of an NvMapObject from it's ID (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_FROM_ID)
|
||||||
*/
|
*/
|
||||||
void FromId(IoctlData &buffer);
|
NvStatus FromId(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This allocates memory for an NvMapObject (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_ALLOC)
|
* @brief This allocates memory for an NvMapObject (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_ALLOC)
|
||||||
*/
|
*/
|
||||||
void Alloc(IoctlData &buffer);
|
NvStatus Alloc(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This frees previously allocated memory (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_FREE)
|
* @brief This frees previously allocated memory (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_FREE)
|
||||||
*/
|
*/
|
||||||
void Free(IoctlData &buffer);
|
NvStatus Free(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This returns a particular parameter from an NvMapObject (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_PARAM)
|
* @brief This returns a particular parameter from an NvMapObject (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_PARAM)
|
||||||
*/
|
*/
|
||||||
void Param(IoctlData &buffer);
|
NvStatus Param(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This returns the ID of an NvMapObject from it's handle (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_GET_ID)
|
* @brief This returns the ID of an NvMapObject from it's handle (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_GET_ID)
|
||||||
*/
|
*/
|
||||||
void GetId(IoctlData &buffer);
|
NvStatus GetId(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
|
||||||
|
|
||||||
NVDEVICE_DECL(
|
NVDEVICE_DECL(
|
||||||
NVFUNC(0x0101, NvMap, Create),
|
NVFUNC(0x0101, NvMap, Create),
|
||||||
|
Loading…
Reference in New Issue
Block a user