NVDRV IOCTL Refactor

Buffer -> Span + All buffers as arguments + Return -> NvStatus + Print Service Names + Function Names
This commit is contained in:
◱ PixelyIon 2020-09-19 20:15:17 +05:30 committed by ◱ PixelyIon
parent 70d67ef563
commit a5fece8020
15 changed files with 407 additions and 429 deletions

View File

@ -1,3 +1,6 @@
# Skyline Proguard Rules
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Retain all classes within Skyline for traces + JNI access + Serializable classes
-keep class emu.skyline.*

View File

@ -187,7 +187,7 @@ namespace skyline {
}
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)
throw exception("Invalid size");
std::array<u8, Size> result;
@ -198,9 +198,23 @@ namespace skyline {
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);
}
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));
}
}
/**

View File

@ -33,7 +33,6 @@ namespace skyline::service::nvdrv {
Result INvDrvServices::Ioctl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto fd = 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);
@ -41,29 +40,25 @@ namespace skyline::service::nvdrv {
cmd &= 0xFFFF;
try {
std::optional<kernel::ipc::IpcBuffer> buffer{std::nullopt};
if (request.inputBuf.empty() || request.outputBuf.empty()) {
if (request.inputBuf.empty()) {
device::IoctlData data(request.outputBuf.at(0));
device->HandleIoctl(cmd, data);
response.Push(data.status);
} else {
device::IoctlData data(request.inputBuf.at(0));
device->HandleIoctl(cmd, data);
response.Push(data.status);
}
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.at(0).address == request.outputBuf.at(0).address) {
buffer = request.inputBuf.at(0);
} else {
device::IoctlData data(request.inputBuf.at(0), request.outputBuf.at(0));
device->HandleIoctl(cmd, data);
response.Push(data.status);
throw exception("IOCTL Input Buffer != Output Buffer");
}
} catch (const std::out_of_range &) {
throw exception("IOCTL was requested on an invalid file descriptor");
}
return {};
response.Push(device->HandleIoctl(cmd, device::IoctlType::Ioctl, std::span<u8>(reinterpret_cast<u8 *>(buffer->address), buffer->size), {}));
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) {

View File

@ -4,29 +4,33 @@
#include "nvdevice.h"
namespace skyline::service::nvdrv::device {
std::string NvDevice::GetName() {
int status{};
size_t length{};
auto mangledName{typeid(*this).name()};
const std::string &NvDevice::GetName() {
if (name.empty()) {
auto mangledName = typeid(*this).name();
std::unique_ptr<char, decltype(&std::free)> demangled{ abi::__cxa_demangle(mangledName, nullptr, &length, &status), std::free};
int status{};
size_t length{};
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) {
std::function<void(IoctlData &)> function;
NvStatus NvDevice::HandleIoctl(u32 cmd, IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
std::pair<std::function<NvStatus(IoctlType, std::span<u8>, std::span<u8>)>, std::string_view> function;
try {
function = GetServiceFunction(cmd);
function = GetIoctlFunction(cmd);
state.logger->Debug("IOCTL @ {}: {}", GetName(), function.second);
} catch (std::out_of_range &) {
state.logger->Warn("Cannot find IOCTL for device '{}': 0x{:X}", GetName(), cmd);
input.status = NvStatus::NotImplemented;
return;
return NvStatus::NotImplemented;
}
try {
function(input);
return function.first(type, buffer, inlineBuffer);
} catch (std::exception &e) {
throw exception("{} (Device: {})", e.what(), GetName());
}
exit(0);
}
}

View File

@ -8,12 +8,13 @@
#include <kernel/ipc.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(...) \
NVDEVICE_DECL_AUTO(functions, frz::make_unordered_map({__VA_ARGS__})); \
std::function<void(IoctlData &)> GetServiceFunction(u32 index) { \
return std::bind(functions.at(index), this, std::placeholders::_1); \
#define NVDEVICE_DECL(...) \
NVDEVICE_DECL_AUTO(functions, frz::make_unordered_map({__VA_ARGS__})); \
std::pair<std::function<NvStatus(IoctlType, std::span<u8>, std::span<u8>)>, std::string_view> GetIoctlFunction(u32 id) { \
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 {
@ -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 {
std::vector<kernel::ipc::InputBuffer> input; //!< A vector of all input IOCTL buffers
std::vector<kernel::ipc::OutputBuffer> output; //!< A vector of all output IOCTL buffers
NvStatus status{NvStatus::Success}; //!< The error code that is returned to the application
/**
* @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}) {}
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
};
/**
* @brief NvDevice is the base class that all /dev/nv* devices inherit from
*/
class NvDevice {
private:
std::string name; //!< The name of the device
protected:
const DeviceState &state; //!< The state of the device
@ -104,19 +73,19 @@ namespace skyline::service::nvdrv::device {
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
* @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
* @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) {
return nullptr;

View File

@ -11,20 +11,21 @@
namespace skyline::service::nvdrv::device {
NvHostAsGpu::NvHostAsGpu(const DeviceState &state) : NvDevice(state) {}
void NvHostAsGpu::BindChannel(IoctlData &buffer) {
struct Data {
u32 fd;
} &channelInfo = state.process->GetReference<Data>(buffer.input.at(0).address);
NvStatus NvHostAsGpu::BindChannel(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
return NvStatus::Success;
}
void NvHostAsGpu::AllocSpace(IoctlData &buffer) {
NvStatus NvHostAsGpu::AllocSpace(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
struct Data {
u32 pages;
u32 pageSize;
u32 flags;
u32 pages; // In
u32 pageSize; // In
u32 flags; // In
u32 _pad_;
u64 offset;
} region = state.process->GetObject<Data>(buffer.input.at(0).address);
union {
u64 offset; // InOut
u64 align; // In
};
} region = util::As<Data>(buffer);
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) {
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) {
auto offset = state.process->GetObject<u64>(buffer.input.at(0).address);
NvStatus NvHostAsGpu::UnmapBuffer(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
u64 offset{util::As<u64>(buffer)};
if (!state.gpu->memoryManager.Unmap(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 {
u32 flags;
u32 kind;
u32 nvmapHandle;
u32 pageSize;
u64 bufferOffset;
u64 mappingSize;
u64 offset;
} region = state.process->GetObject<Data>(buffer.input.at(0).address);
u32 flags; // In
u32 kind; // In
u32 nvmapHandle; // In
u32 pageSize; // InOut
u64 bufferOffset; // In
u64 mappingSize; // In
u64 offset; // InOut
} &data = util::As<Data>(buffer);
if (!region.nvmapHandle)
return;
try {
auto driver = nvdrv::driver.lock();
auto nvmap = driver->nvMap.lock();
auto mapping = nvmap->handleTable.at(data.nvmapHandle);
auto driver = nvdrv::driver.lock();
auto nvmap = driver->nvMap.lock();
auto mapping = nvmap->handleTable.at(region.nvmapHandle);
u64 mapPhysicalAddress = data.bufferOffset + mapping->address;
u64 mapSize = data.mappingSize ? data.mappingSize : mapping->size;
u64 mapPhysicalAddress = region.bufferOffset + mapping->address;
u64 mapSize = region.mappingSize ? region.mappingSize : mapping->size;
if (data.flags & 1)
data.offset = state.gpu->memoryManager.MapFixed(data.offset, mapPhysicalAddress, mapSize);
else
data.offset = state.gpu->memoryManager.MapAllocate(mapPhysicalAddress, mapSize);
if (region.flags & 1)
region.offset = state.gpu->memoryManager.MapFixed(region.offset, mapPhysicalAddress, mapSize);
else
region.offset = state.gpu->memoryManager.MapAllocate(mapPhysicalAddress, mapSize);
if (data.offset == 0) {
state.logger->Warn("Failed to map GPU address space region!");
return NvStatus::BadParameter;
}
if (region.offset == 0) {
state.logger->Warn("Failed to map GPU address space region!");
buffer.status = NvStatus::BadParameter;
return NvStatus::Success;
} catch (const std::out_of_range &) {
state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.nvmapHandle);
return NvStatus::BadParameter;
}
state.process->WriteMemory(region, buffer.output.at(0).address);
}
void NvHostAsGpu::GetVaRegions(IoctlData &buffer) {
NvStatus NvHostAsGpu::GetVaRegions(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
/*
struct Data {
u64 _pad0_;
u32 bufferSize;
u32 bufferSize; // InOut
u32 _pad1_;
struct {
@ -93,38 +99,40 @@ namespace skyline::service::nvdrv::device {
u32 page_size;
u32 pad;
u64 pages;
} regions[2];
} &regionInfo = state.process->GetReference<Data>(buffer.input.at(0).address);
state.process->WriteMemory(regionInfo, buffer.output.at(0).address);
} regions[2]; // Out
} &regionInfo = util::As<Data>(buffer);
*/
return NvStatus::Success;
}
void NvHostAsGpu::InitializeEx(IoctlData &buffer) {
NvStatus NvHostAsGpu::AllocAsEx(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
/*
struct Data {
u32 bigPageSize;
i32 asFd;
u32 flags;
u32 reserved;
u64 vaRangeStart;
u64 vaRangeEnd;
u64 vaRangeSplit;
} addressSpace = state.process->GetObject<Data>(buffer.input.at(0).address);
u32 bigPageSize; // In
i32 asFd; // In
u32 flags; // In
u32 reserved; // In
u64 vaRangeStart; // In
u64 vaRangeEnd; // In
u64 vaRangeSplit; // In
} 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 {
u16 flags;
u16 kind;
u32 nvmapHandle;
u32 mapOffset;
u32 gpuOffset;
u32 pages;
u16 flags; // In
u16 kind; // In
u32 nvmapHandle; // In
u32 mapOffset; // In
u32 gpuOffset; // In
u32 pages; // In
};
constexpr u32 MinAlignmentShift{0x10}; // This shift is applied to all addresses passed to Remap
size_t entryCount{buffer.input.at(0).size / sizeof(Entry)};
std::span entries(state.process->GetPointer<Entry>(buffer.input.at(0).address), entryCount);
auto entries{util::AsSpan<Entry>(buffer)};
for (auto entry : entries) {
try {
auto driver = nvdrv::driver.lock();
@ -136,10 +144,12 @@ namespace skyline::service::nvdrv::device {
u64 mapSize = static_cast<u64>(entry.pages) << MinAlignmentShift;
state.gpu->memoryManager.MapFixed(mapAddress, mapPhysicalAddress, mapSize);
} catch (const std::exception &e) {
buffer.status = NvStatus::BadValue;
return;
} catch (const std::out_of_range &) {
state.logger->Warn("Invalid NvMap handle: 0x{:X}", entry.nvmapHandle);
return NvStatus::BadParameter;
}
}
return NvStatus::Success;
}
}

View File

@ -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)
*/
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)
*/
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)
*/
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)
*/
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)
*/
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)
*/
void Remap(IoctlData &buffer);
NvStatus Remap(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
NVDEVICE_DECL(
NVFUNC(0x4101, NvHostAsGpu, BindChannel),
@ -54,7 +54,7 @@ namespace skyline::service::nvdrv::device {
NVFUNC(0x4105, NvHostAsGpu, UnmapBuffer),
NVFUNC(0x4106, NvHostAsGpu, Modify),
NVFUNC(0x4108, NvHostAsGpu, GetVaRegions),
NVFUNC(0x4109, NvHostAsGpu, InitializeEx),
NVFUNC(0x4109, NvHostAsGpu, AllocAsEx),
NVFUNC(0x4114, NvHostAsGpu, Remap)
)
};

View File

@ -16,14 +16,18 @@ namespace skyline::service::nvdrv::device {
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 {
u64 address;
u32 numEntries;
u64 address; // In
u32 numEntries; // In
union {
struct __attribute__((__packed__)) {
bool fenceWait : 1;
@ -35,46 +39,50 @@ namespace skyline::service::nvdrv::device {
bool incrementWithValue : 1;
};
u32 raw;
} flags;
Fence fence;
} &args = state.process->GetReference<Data>(buffer.output.at(0).address);
} flags; // In
Fence fence; // InOut
} &data = util::As<Data>(buffer);
auto driver = nvdrv::driver.lock();
auto &hostSyncpoint = driver->hostSyncpoint;
if (args.flags.fenceWait) {
if (args.flags.incrementWithValue) {
buffer.status = NvStatus::BadValue;
return;
}
if (data.flags.fenceWait) {
if (data.flags.incrementWithValue)
return NvStatus::BadValue;
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");
}
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);
args.fence.value = hostSyncpoint.IncrementSyncpointMaxExt(args.fence.id, increment);
u32 increment = (data.flags.fenceIncrement ? 2 : 0) + (data.flags.incrementWithValue ? data.fence.value : 0);
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");
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) {
auto priority = state.process->GetObject<NvChannelPriority>(buffer.input.at(0).address);
switch (priority) {
NvStatus NvHostChannel::SetPriority(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
switch (util::As<NvChannelPriority>(buffer)) {
case NvChannelPriority::Low:
timeslice = 1300;
break;
@ -85,23 +93,29 @@ namespace skyline::service::nvdrv::device {
timeslice = 5200;
break;
}
return NvStatus::Success;
}
void NvHostChannel::AllocGpfifoEx2(IoctlData &buffer) {
NvStatus NvHostChannel::AllocGpfifoEx2(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
struct Data {
u32 numEntries;
u32 numJobs;
u32 flags;
Fence fence;
u32 reserved[3];
} &args = state.process->GetReference<Data>(buffer.input.at(0).address);
u32 numEntries; // In
u32 numJobs; // In
u32 flags; // In
Fence fence; // Out
u32 reserved[3]; // In
} &data = util::As<Data>(buffer);
auto driver = nvdrv::driver.lock();
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) {
switch (eventId) {

View File

@ -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)
*/
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)
*/
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)
*/
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)
*/
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)
*/
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)
*/
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)
*/
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)
*/
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)
*/
void SetUserData(IoctlData &buffer);
NvStatus SetUserData(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
std::shared_ptr<type::KEvent> QueryEvent(u32 eventId);

View File

@ -72,91 +72,79 @@ namespace skyline::service::nvdrv::device {
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 {
Fence fence;
u32 timeout;
EventValue value;
} &args = state.process->GetReference<Data>(buffer.output.at(0).address);
Fence fence; // In
u32 timeout; // In
EventValue value; // InOut
} &data = util::As<Data>(buffer);
if (args.fence.id >= constant::MaxHwSyncpointCount) {
buffer.status = NvStatus::BadValue;
return;
}
if (data.fence.id >= constant::MaxHwSyncpointCount)
return NvStatus::BadValue;
if (args.timeout == 0) {
buffer.status = NvStatus::Timeout;
return;
}
if (data.timeout == 0)
return NvStatus::Timeout;
auto driver = nvdrv::driver.lock();
auto &hostSyncpoint = driver->hostSyncpoint;
// Check if the syncpoint has already expired using the last known values
if (hostSyncpoint.HasSyncpointExpired(args.fence.id, args.fence.value)) {
args.value.val = hostSyncpoint.ReadSyncpointMinValue(args.fence.id);
return;
if (hostSyncpoint.HasSyncpointExpired(data.fence.id, data.fence.value)) {
data.value.val = hostSyncpoint.ReadSyncpointMinValue(data.fence.id);
return NvStatus::Success;
}
// Sync the syncpoint with the GPU then check again
auto minVal = hostSyncpoint.UpdateMin(args.fence.id);
if (hostSyncpoint.HasSyncpointExpired(args.fence.id, args.fence.value)) {
args.value.val = minVal;
return;
auto minVal = hostSyncpoint.UpdateMin(data.fence.id);
if (hostSyncpoint.HasSyncpointExpired(data.fence.id, data.fence.value)) {
data.value.val = minVal;
return NvStatus::Success;
}
u32 userEventId{};
if (async) {
if (args.value.val >= constant::NvHostEventCount || !events.at(args.value.val)) {
buffer.status = NvStatus::BadValue;
return;
}
if (data.value.val >= constant::NvHostEventCount || !events.at(data.value.val))
return NvStatus::BadValue;
userEventId = args.value.val;
userEventId = data.value.val;
} else {
args.fence.value = 0;
data.fence.value = 0;
userEventId = FindFreeEvent(args.fence.id);
userEventId = FindFreeEvent(data.fence.id);
}
auto event = &*events.at(userEventId);
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);
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, args.fence.id);
event->Wait(state.gpu, args.fence);
args.value.val = 0;
data.value.val = 0;
if (async) {
args.value.syncpointIdAsync = args.fence.id;
data.value.syncpointIdAsync = data.fence.id;
} else {
args.value.syncpointIdNonAsync = args.fence.id;
args.value.nonAsync = true;
data.value.syncpointIdNonAsync = data.fence.id;
data.value.nonAsync = true;
}
args.value.val |= userEventId;
data.value.val |= userEventId;
buffer.status = NvStatus::Timeout;
return;
return NvStatus::Timeout;
} else {
buffer.status = NvStatus::BadValue;
return;
return NvStatus::BadValue;
}
}
void NvHostCtrl::GetConfig(IoctlData &buffer) {
buffer.status = NvStatus::BadValue;
NvStatus NvHostCtrl::GetConfig(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
return NvStatus::BadValue;
}
void NvHostCtrl::EventSignal(IoctlData &buffer) {
auto userEventId = static_cast<u16>(state.process->GetObject<u32>(buffer.input.at(0).address));
NvStatus NvHostCtrl::EventSignal(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
auto userEventId{util::As<u32>(buffer)};
state.logger->Debug("Signalling nvhost event: {}", userEventId);
if (userEventId >= constant::NvHostEventCount || !events.at(userEventId)) {
buffer.status = NvStatus::BadValue;
return;
}
if (userEventId >= constant::NvHostEventCount || !events.at(userEventId))
return NvStatus::BadValue;
auto &event = *events.at(userEventId);
@ -171,30 +159,32 @@ namespace skyline::service::nvdrv::device {
auto driver = nvdrv::driver.lock();
auto &hostSyncpoint = driver->hostSyncpoint;
hostSyncpoint.UpdateMin(event.fence.id);
return NvStatus::Success;
}
void NvHostCtrl::EventWait(IoctlData &buffer) {
EventWaitImpl(buffer, false);
NvStatus NvHostCtrl::EventWait(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
return EventWaitImpl(buffer, false);
}
void NvHostCtrl::EventWaitAsync(IoctlData &buffer) {
EventWaitImpl(buffer, true);
NvStatus NvHostCtrl::EventWaitAsync(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
return EventWaitImpl(buffer, true);
}
void NvHostCtrl::EventRegister(IoctlData &buffer) {
auto userEventId = state.process->GetObject<u32>(buffer.input.at(0).address);
NvStatus NvHostCtrl::EventRegister(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
auto userEventId{util::As<u32>(buffer)};
state.logger->Debug("Registering nvhost event: {}", userEventId);
auto &event = events.at(userEventId);
if (event)
throw exception("Recreating events is unimplemented");
event = NvHostEvent(state);
return NvStatus::Success;
}
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);
if (event && event->fence.id == (eventValue.nonAsync ? eventValue.syncpointIdNonAsync : eventValue.syncpointIdAsync))

View File

@ -87,7 +87,7 @@ namespace skyline {
*/
u32 FindFreeEvent(u32 syncpointId);
void EventWaitImpl(IoctlData &buffer, bool async);
NvStatus EventWaitImpl(std::span<u8> buffer, bool async);
public:
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)
*/
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)
*/
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)
*/
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)
*/
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)
*/
void EventRegister(IoctlData &buffer);
NvStatus EventRegister(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
std::shared_ptr<type::KEvent> QueryEvent(u32 eventId);

View File

@ -7,13 +7,13 @@
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) {}
void NvHostCtrlGpu::ZCullGetCtxSize(IoctlData &buffer) {
u32 size = 0x1;
state.process->WriteMemory(size, buffer.output[0].address);
NvStatus NvHostCtrlGpu::ZCullGetCtxSize(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
util::As<u32>(buffer) = 0x1;
return NvStatus::Success;
}
void NvHostCtrlGpu::ZCullGetInfo(IoctlData &buffer) {
struct {
NvStatus NvHostCtrlGpu::ZCullGetInfo(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
struct ZCullInfo {
u32 widthAlignPixels{0x20};
u32 heightAlignPixels{0x20};
u32 pixelSquaresByAliquots{0x400};
@ -26,119 +26,84 @@ namespace skyline::service::nvdrv::device {
u32 subregionCount{0x10};
} 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 {
u32 arch; // 0x120 (NVGPU_GPU_ARCH_GM200)
u32 impl; // 0xB (NVGPU_GPU_IMPL_GM20B) or 0xE (NVGPU_GPU_IMPL_GM20B_B)
u32 rev; // 0xA1 (Revision A1)
u32 numGpc; // 0x1
u64 l2CacheSize; // 0x40000
u64 onBoardVideoMemorySize; // 0x0 (not used)
u32 numTpcPerGpc; // 0x2
u32 busType; // 0x20 (NVGPU_GPU_BUS_TYPE_AXI)
u32 bigPageSize; // 0x20000
u32 compressionPageSize; // 0x20000
u32 pdeCoverageBitCount; // 0x1B
u32 availableBigPageSizes; // 0x30000
u32 gpcMask; // 0x1
u32 smArchSmVersion; // 0x503 (Maxwell Generation 5.0.3)
u32 smArchSpaVersion; // 0x503 (Maxwell Generation 5.0.3)
u32 smArchWarpCount; // 0x80
u32 gpuVaBitCount; // 0x28
u32 reserved; // NULL
u64 flags; // 0x55 (HAS_SYNCPOINTS | SUPPORT_SPARSE_ALLOCS | SUPPORT_CYCLE_STATS | SUPPORT_CYCLE_STATS_SNAPSHOT)
u32 twodClass; // 0x902D (FERMI_TWOD_A)
u32 threedClass; // 0xB197 (MAXWELL_B)
u32 computeClass; // 0xB1C0 (MAXWELL_COMPUTE_B)
u32 gpfifoClass; // 0xB06F (MAXWELL_CHANNEL_GPFIFO_A)
u32 inlineToMemoryClass; // 0xA140 (KEPLER_INLINE_TO_MEMORY_B)
u32 dmaCopyClass; // 0xB0B5 (MAXWELL_DMA_COPY_A)
u32 maxFbpsCount; // 0x1
u32 fbpEnMask; // 0x0 (disabled)
u32 maxLtcPerFbp; // 0x2
u32 maxLtsPerLtc; // 0x1
u32 maxTexPerTpc; // 0x0 (not supported)
u32 maxGpcCount; // 0x1
u32 ropL2EnMask0; // 0x21D70 (fuse_status_opt_rop_l2_fbp_r)
u32 ropL2EnMask1; // 0x0
u64 chipName; // 0x6230326D67 ("gm20b")
u64 grCompbitStoreBaseHw; // 0x0 (not supported)
u32 arch{0x120}; // NVGPU_GPU_ARCH_GM200
u32 impl{0xB}; // 0xB (NVGPU_GPU_IMPL_GM20B) or 0xE (NVGPU_GPU_IMPL_GM20B_B)
u32 rev{0xA1};
u32 numGpc{0x1};
u64 l2CacheSize{0x40000};
u64 onBoardVideoMemorySize{}; // UMA
u32 numTpcPerGpc{0x2};
u32 busType{0x20}; // NVGPU_GPU_BUS_TYPE_AXI
u32 bigPageSize{0x20000};
u32 compressionPageSize{0x20000};
u32 pdeCoverageBitCount{0x1B};
u32 availableBigPageSizes{0x30000};
u32 gpcMask{0x1};
u32 smArchSmVersion{0x503}; // Maxwell Generation 5.0.3
u32 smArchSpaVersion{0x503}; // Maxwell Generation 5.0.3
u32 smArchWarpCount{0x80};
u32 gpuVaBitCount{0x28};
u32 reserved{};
u64 flags{0x55}; // HAS_SYNCPOINTS | SUPPORT_SPARSE_ALLOCS | SUPPORT_CYCLE_STATS | SUPPORT_CYCLE_STATS_SNAPSHOT
u32 twodClass{0x902D}; // FERMI_TWOD_A
u32 threedClass{0xB197}; // MAXWELL_B
u32 computeClass{0xB1C0}; // MAXWELL_COMPUTE_B
u32 gpfifoClass{0xB06F}; // MAXWELL_CHANNEL_GPFIFO_A
u32 inlineToMemoryClass{0xA140}; // KEPLER_INLINE_TO_MEMORY_B
u32 dmaCopyClass{0xA140}; // MAXWELL_DMA_COPY_A
u32 maxFbpsCount{0x1}; // 0x1
u32 fbpEnMask{}; // Disabled
u32 maxLtcPerFbp{0x2};
u32 maxLtsPerLtc{0x1};
u32 maxTexPerTpc{}; // Not Supported
u32 maxGpcCount{0x1};
u32 ropL2EnMask0{0x21D70}; // fuse_status_opt_rop_l2_fbp_r
u32 ropL2EnMask1{};
u64 chipName{util::MakeMagic<u64>("gm20b")};
u64 grCompbitStoreBaseHw{}; // Not Supported
};
struct Data {
u64 gpuCharacteristicsBufSize; // InOut
u64 gpuCharacteristicsBufAddr; // In
GpuCharacteristics gpuCharacteristics; // Out
} data = state.process->GetObject<Data>(buffer.input[0].address);
} &data = util::As<Data>(buffer);
data.gpuCharacteristics = {
.arch = 0x120,
.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
};
if (data.gpuCharacteristicsBufSize < sizeof(GpuCharacteristics))
return NvStatus::InvalidSize;
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 {
u32 maskBufSize; // In
u32 reserved[3]; // In
u64 maskBuf; // Out
} data = state.process->GetObject<Data>(buffer.input[0].address);
} &data = util::As<Data>(buffer);
if (data.maskBufSize)
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 {
u32 slot; // Out
u32 mask; // Out
} data = {
.slot = 0x07,
.mask = 0x01
};
state.process->WriteMemory(data, buffer.output[0].address);
u32 slot{0x07}; // Out
u32 mask{0x01}; // Out
} data;
util::As<Data>(buffer) = data;
return NvStatus::Success;
}
std::shared_ptr<type::KEvent> NvHostCtrlGpu::QueryEvent(u32 eventId) {
@ -151,5 +116,4 @@ namespace skyline::service::nvdrv::device {
return nullptr;
}
}
}

View File

@ -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)
*/
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)
*/
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)
*/
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)
*/
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)
*/
void GetActiveSlotMask(IoctlData &buffer);
NvStatus GetActiveSlotMask(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
std::shared_ptr<type::KEvent> QueryEvent(u32 eventId);

View File

@ -9,138 +9,153 @@ namespace skyline::service::nvdrv::device {
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 {
u32 size; // In
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);
data.handle = handleIndex++;
state.process->WriteMemory(data, buffer.output[0].address);
state.logger->Debug("Create: Input: Size: 0x{:X}, Output: Handle: 0x{:X}, Status: {}", data.size, data.handle, buffer.status);
state.logger->Debug("Size: 0x{:X} -> Handle: 0x{:X}", data.size, data.handle);
return NvStatus::Success;
}
void NvMap::FromId(IoctlData &buffer) {
NvStatus NvMap::FromId(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
struct Data {
u32 id; // In
u32 handle; // Out
} data = state.process->GetObject<Data>(buffer.input[0].address);
} &data = util::As<Data>(buffer);
bool found{};
for (const auto &object : handleTable) {
if (object.second->id == data.id) {
data.handle = object.first;
found = true;
break;
state.logger->Debug("ID: 0x{:X} -> Handle: 0x{:X}", data.id, data.handle);
return NvStatus::Success;
}
}
if (found)
state.process->WriteMemory(data, buffer.output[0].address);
else
buffer.status = NvStatus::BadValue;
state.logger->Debug("FromId: Input: Handle: 0x{:X}, Output: ID: 0x{:X}, Status: {}", data.handle, data.id, buffer.status);
state.logger->Warn("Handle not found for ID: 0x{:X}", data.id);
return NvStatus::BadValue;
}
void NvMap::Alloc(IoctlData &buffer) {
NvStatus NvMap::Alloc(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
struct Data {
u32 handle; // In
u32 heapMask; // In
u32 flags; // In
u32 align; // In
u8 kind; // In
u8 kind; // In
u8 _pad0_[7];
u64 address; // InOut
} data = state.process->GetObject<Data>(buffer.input[0].address);
} &data = util::As<Data>(buffer);
auto &object = handleTable.at(data.handle);
object->heapMask = data.heapMask;
object->flags = data.flags;
object->align = data.align;
object->kind = data.kind;
object->address = data.address;
object->status = NvMapObject::Status::Allocated;
try {
auto &object = handleTable.at(data.handle);
object->heapMask = data.heapMask;
object->flags = data.flags;
object->align = data.align;
object->kind = data.kind;
object->address = data.address;
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 {
u32 handle; // In
u32 _pad0_;
u64 address; // Out
u32 size; // Out
u32 flags; // Out
} data = state.process->GetObject<Data>(buffer.input[0].address);
} &data = util::As<Data>(buffer);
const auto &object = handleTable.at(data.handle);
if (object.use_count() > 1) {
data.address = static_cast<u32>(object->address);
data.flags = 0x0;
} else {
data.address = 0x0;
data.flags = 0x1; // Not free yet
try {
const auto &object = handleTable.at(data.handle);
if (object.use_count() > 1) {
data.address = static_cast<u32>(object->address);
data.flags = 0x0;
} else {
data.address = 0x0;
data.flags = 0x1; // Not free yet
}
data.size = object->size;
handleTable.erase(data.handle);
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;
}
data.size = object->size;
handleTable.erase(data.handle);
state.process->WriteMemory(data, buffer.output[0].address);
}
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
struct Data {
u32 handle; // In
Parameter parameter; // In
u32 result; // Out
} data = state.process->GetObject<Data>(buffer.input[0].address);
} &data = util::As<Data>(buffer);
try {
auto &object = handleTable.at(data.handle);
switch (data.parameter) {
case Parameter::Size:
data.result = object->size;
break;
case Parameter::Alignment:
data.result = object->align;
break;
case Parameter::HeapMask:
data.result = object->heapMask;
break;
case Parameter::Kind:
data.result = object->kind;
break;
case Parameter::Compr:
data.result = 0;
break;
default:
buffer.status = NvStatus::NotImplemented;
return;
state.logger->Warn("Parameter not implemented: 0x{:X}", data.parameter);
return NvStatus::NotImplemented;
}
state.process->WriteMemory(data, buffer.output[0].address);
state.logger->Debug("Param: Input: Handle: 0x{:X}, Parameter: {}, Output: Result: 0x{:X}, Status: {}", data.handle, data.parameter, data.result, buffer.status);
} catch (std::exception &e) {
buffer.status = NvStatus::BadParameter;
return;
state.logger->Debug("Handle: 0x{:X}, Parameter: {} -> Result: 0x{:X}", data.handle, data.parameter, data.result);
return NvStatus::Success;
} catch (const std::out_of_range &) {
state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.handle);
return NvStatus::BadParameter;
}
}
void NvMap::GetId(IoctlData &buffer) {
NvStatus NvMap::GetId(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
struct Data {
u32 id; // Out
u32 handle; // In
} data = state.process->GetObject<Data>(buffer.input[0].address);
} &data = util::As<Data>(buffer);
data.id = handleTable.at(data.handle)->id;
state.process->WriteMemory(data, buffer.output[0].address);
state.logger->Debug("GetId: Input: Handle: 0x{:X}, Output: ID: 0x{:X}, Status: {}", data.handle, data.id, buffer.status);
try {
data.id = handleTable.at(data.handle)->id;
state.logger->Debug("Handle: 0x{:X} -> ID: 0x{:X}", data.handle, data.id);
return NvStatus::Success;
} catch (const std::out_of_range &) {
state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.handle);
return NvStatus::BadParameter;
}
}
}

View File

@ -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)
*/
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)
*/
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)
*/
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)
*/
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)
*/
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)
*/
void GetId(IoctlData &buffer);
NvStatus GetId(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
NVDEVICE_DECL(
NVFUNC(0x0101, NvMap, Create),