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 # 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.*

View File

@ -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));
}
} }
/** /**

View File

@ -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) {

View File

@ -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);
} }
} }

View File

@ -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;

View File

@ -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
} &regionInfo = state.process->GetReference<Data>(buffer.input.at(0).address); } &regionInfo = 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;
} }
} }

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) * @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)
) )
}; };

View File

@ -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) {

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) * @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);

View File

@ -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))

View File

@ -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);

View File

@ -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;
} }
} }
} }

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) * @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);

View File

@ -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;
}
} }
} }

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) * @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),