Add support for Ioctl2/3 and improve debug logging

This commit is contained in:
Billy Laws 2021-07-17 22:23:26 +01:00 committed by ◱ Mark
parent 15db64671f
commit 020aa0e43a
15 changed files with 203 additions and 30 deletions

View File

@ -6,7 +6,13 @@
#include "driver.h"
#include "devices/nvdevice.h"
#define NVRESULT(x) [&response](NvResult err) { response.Push<NvResult>(err); return Result{}; }(x)
#define NVRESULT(x) [&response, this](NvResult err) { \
if (err != NvResult::Success) \
state.logger->Debug("IOCTL Failed: {}", err); \
\
response.Push<NvResult>(err); \
return Result{}; \
} (x)
namespace skyline::service::nvdrv {
INvDrvServices::INvDrvServices(const DeviceState &state, ServiceManager &manager, Driver &driver, const SessionPermissions &perms) : BaseService(state, manager), driver(driver), ctx(SessionContext{.perms = perms}) {}
@ -29,29 +35,34 @@ namespace skyline::service::nvdrv {
return NVRESULT(NvResult::Success);
}
Result INvDrvServices::Ioctl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto fd{request.Pop<FileDescriptor>()};
auto ioctl{request.Pop<IoctlDescriptor>()};
auto inBuf{request.inputBuf.at(0)};
auto outBuf{request.outputBuf.at(0)};
static NvResultValue<span<u8>> GetMainIoctlBuffer(IoctlDescriptor ioctl, span<u8> inBuf, span<u8> outBuf) {
if (ioctl.in && inBuf.size() < ioctl.size)
return NVRESULT(NvResult::InvalidSize);
return NvResult::InvalidSize;
if (ioctl.out && outBuf.size() < ioctl.size)
return NVRESULT(NvResult::InvalidSize);
return NvResult::InvalidSize;
if (ioctl.in && ioctl.out) {
if (outBuf.size() < inBuf.size())
return NVRESULT(NvResult::InvalidSize);
return NvResult::InvalidSize;
// Copy in buf to out buf for inout ioctls to avoid needing to pass around two buffers everywhere
if (outBuf.data() != inBuf.data())
outBuf.copy_from(inBuf, ioctl.size);
}
return NVRESULT(driver.Ioctl(fd, ioctl, ioctl.out ? outBuf : inBuf));
return ioctl.out ? outBuf : inBuf;
}
Result INvDrvServices::Ioctl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto fd{request.Pop<FileDescriptor>()};
auto ioctl{request.Pop<IoctlDescriptor>()};
auto buf{GetMainIoctlBuffer(ioctl, request.inputBuf.at(0), request.outputBuf.at(0))};
if (!buf)
return NVRESULT(buf);
else
return NVRESULT(driver.Ioctl(fd, ioctl, *buf));
}
Result INvDrvServices::Close(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
@ -85,6 +96,34 @@ namespace skyline::service::nvdrv {
}
}
Result INvDrvServices::Ioctl2(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto fd{request.Pop<FileDescriptor>()};
auto ioctl{request.Pop<IoctlDescriptor>()};
// The inline buffer is technically not required
auto inlineBuf{request.inputBuf.size() > 1 ? request.inputBuf.at(1) : span<u8>{}};
auto buf{GetMainIoctlBuffer(ioctl, request.inputBuf.at(0), request.outputBuf.at(0))};
if (!buf)
return NVRESULT(buf);
else
return NVRESULT(driver.Ioctl2(fd, ioctl, *buf, inlineBuf));
}
Result INvDrvServices::Ioctl3(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto fd{request.Pop<FileDescriptor>()};
auto ioctl{request.Pop<IoctlDescriptor>()};
// The inline buffer is technically not required
auto inlineBuf{request.outputBuf.size() > 1 ? request.outputBuf.at(1) : span<u8>{}};
auto buf{GetMainIoctlBuffer(ioctl, request.inputBuf.at(0), request.outputBuf.at(0))};
if (!buf)
return NVRESULT(buf);
else
return NVRESULT(driver.Ioctl3(fd, ioctl, *buf, inlineBuf));
}
Result INvDrvServices::SetAruid(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
return NVRESULT(NvResult::Success);
}

View File

@ -63,13 +63,13 @@ namespace skyline::service::nvdrv {
* @brief Perform an IOCTL on the specified FD with an extra input buffer
* @url https://switchbrew.org/wiki/NV_services#Ioctl2
*/
// Result Ioctl2(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
Result Ioctl2(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Perform an IOCTL on the specified FD with an extra output buffer
* @url https://switchbrew.org/wiki/NV_services#Ioctl3
*/
// Result Ioctl3(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
Result Ioctl3(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Enables the graphics firmware memory margin
@ -84,7 +84,8 @@ namespace skyline::service::nvdrv {
SFUNC(0x3, INvDrvServices, Initialize),
SFUNC(0x4, INvDrvServices, QueryEvent),
SFUNC(0x8, INvDrvServices, SetAruid),
SFUNC(0xB, INvDrvServices, Ioctl2),
SFUNC(0xC, INvDrvServices, Ioctl3),
SFUNC(0xD, INvDrvServices, SetGraphicsFirmwareMemoryMarginEnabled)
)
};

View File

@ -3,6 +3,16 @@
#include "deserialisation.h"
#define INLINE_IOCTL_HANDLER_FUNC(type, name, cases) \
PosixResult name::type(IoctlDescriptor cmd, span<u8> buffer, span<u8> inlineBuffer) { \
using className = name; \
switch (cmd.raw) { \
cases; \
default: \
return PosixResult::InappropriateIoctlForDevice; \
} \
}
#define VARIABLE_IOCTL_HANDLER_FUNC(name, cases, variableCases) \
PosixResult name::Ioctl(IoctlDescriptor cmd, span<u8> buffer) { \
using className = name; \
@ -47,6 +57,7 @@
#define IOCTL_CASE_NOARGS(...) IOCTL_CASE_NOARGS_I(__VA_ARGS__)
#define IOCTL_CASE_RESULT(...) IOCTL_CASE_RESULT_I(__VA_ARGS__)
#define VARIABLE_IOCTL_CASE_ARGS_I(out, in, magic, function, name, ...) \
case MetaVariableIoctlDescriptor<out, in, magic, function>::Raw(): { \
using IoctlType = MetaVariableIoctlDescriptor<out, in, magic, function>; \
@ -56,6 +67,16 @@
#define VARIABLE_IOCTL_CASE_ARGS(...) VARIABLE_IOCTL_CASE_ARGS_I(__VA_ARGS__)
#define INLINE_IOCTL_CASE_ARGS_I(out, in, size, magic, function, name, ...) \
case MetaIoctlDescriptor<out, in, size, magic, function>::Raw(): { \
using IoctlType = MetaIoctlDescriptor< out, in, size, magic, function>; \
auto args = DecodeArguments<IoctlType, __VA_ARGS__>(buffer.subspan<0, size>()); \
return std::apply(&className::name, std::tuple_cat(std::make_tuple(this, inlineBuffer), args)); \
}
#define INLINE_IOCTL_CASE_ARGS(...) INLINE_IOCTL_CASE_ARGS_I(__VA_ARGS__)
#define IN false, true
#define OUT true, false
#define INOUT true, true

View File

@ -3,14 +3,21 @@
#undef IOCTL_HANDLER_FUNC
#undef VARIABLE_IOCTL_HANDLER_FUNC
#undef INLINE_IOCTL_HANDLER_FUNC
#undef IOCTL_CASE_ARGS_I
#undef IOCTL_CASE_NOARGS_I
#undef IOCTL_CASE_RESULT_I
#undef IOCTL_CASE_ARGS
#undef IOCTL_CASE_NOARGS
#undef IOCTL_CASE_RESULT
#undef VARIABLE_IOCTL_CASE_ARGS_I
#undef VARIABLE_IOCTL_CASE_ARGS
#undef INLINE_IOCTL_CASE_ARGS_I
#undef INLINE_IOCTL_CASE_ARGS
#undef IN
#undef OUT
#undef INOUT

View File

@ -40,9 +40,13 @@ namespace skyline::service::nvdrv::device {
virtual PosixResult Ioctl(IoctlDescriptor cmd, span<u8> buffer) = 0;
virtual PosixResult Ioctl2(IoctlDescriptor cmd, span<u8> buffer, span<u8> inlineOutput) { return PosixResult::NotSupported; }
virtual PosixResult Ioctl2(IoctlDescriptor cmd, span<u8> buffer, span<u8> inlineOutput) {
return PosixResult::InappropriateIoctlForDevice;
}
virtual PosixResult Ioctl3(IoctlDescriptor cmd, span<u8> buffer, span<u8> inlineInput) { return PosixResult::NotSupported; }
virtual PosixResult Ioctl3(IoctlDescriptor cmd, span<u8> buffer, span<u8> inlineInput) {
return PosixResult::InappropriateIoctlForDevice;
}
virtual std::shared_ptr<kernel::type::KEvent> QueryEvent(u32 eventId) {
return nullptr;

View File

@ -54,6 +54,8 @@ namespace skyline::service::nvdrv::device::nvhost {
}
PosixResult AsGpu::MapBufferEx(In<MappingFlags> flags, In<u32> kind, In<core::NvMap::Handle::Id> handle, InOut<u32> pageSize, In<u64> bufferOffset, In<u64> mappingSize, InOut<u64> offset) {
state.logger->Debug("flags: ( fixed: {}, remap: {} ), kind: {}, handle: {}, pageSize: 0x{:X}, bufferOffset: 0x{:X}, mappingSize: 0x{:X}, offset: 0x{:X}", flags.fixed, flags.remap, kind, handle, pageSize, bufferOffset, mappingSize, offset);
if (flags.remap) {
auto region{regionMap.lower_bound(offset)};
if (region == regionMap.end()) {
@ -95,8 +97,11 @@ namespace skyline::service::nvdrv::device::nvhost {
if (offset == 0) {
state.logger->Warn("Failed to map GPU address space region!");
return PosixResult::InvalidArgument;
}
state.logger->Debug("Mapped to 0x{:X}", offset);
regionMap[offset] = {cpuPtr, size, flags.fixed};
return PosixResult::Success;
@ -107,6 +112,10 @@ namespace skyline::service::nvdrv::device::nvhost {
return PosixResult::Success;
}
PosixResult AsGpu::GetVaRegions3(span<u8> inlineBufer, In<u64> bufAddr, InOut<u32> bufSize, Out<std::array<VaRegion, 2>> vaRegions) {
return GetVaRegions(bufAddr, bufSize, vaRegions);
}
PosixResult AsGpu::AllocAsEx(In<u32> bigPageSize, In<FileDescriptor> asFd, In<u32> flags, In<u64> vaRangeStart, In<u64> vaRangeEnd, In<u64> vaRangeSplit) {
// TODO: create the allocator here
return PosixResult::Success;
@ -152,5 +161,10 @@ namespace skyline::service::nvdrv::device::nvhost {
VARIABLE_IOCTL_CASE_ARGS(INOUT, MAGIC(AsGpuMagic), FUNC(0x14),
Remap, ARGS(AutoSizeSpan<RemapEntry>))
}))
INLINE_IOCTL_HANDLER_FUNC(Ioctl3, AsGpu, ({
INLINE_IOCTL_CASE_ARGS(INOUT, SIZE(0x40), MAGIC(AsGpuMagic), FUNC(0x8),
GetVaRegions3, ARGS(In<u64>, InOut<u32>, Pad<u32>, Out<std::array<VaRegion, 2>>))
}))
#include <services/nvdrv/devices/deserialisation/macro_undef.h>
}

View File

@ -85,6 +85,11 @@ namespace skyline::service::nvdrv::device::nvhost {
*/
PosixResult GetVaRegions(In<u64> bufAddr, InOut<u32> bufSize, Out<std::array<VaRegion, 2>> vaRegions);
/**
* @brief Ioctl3 variant of GetVaRegions
*/
PosixResult GetVaRegions3(span<u8> inlineBuffer, In<u64> bufAddr, InOut<u32> bufSize, Out<std::array<VaRegion, 2>> vaRegions);
/**
* @brief Allocates this address space with the given parameters
* @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_ALLOC_AS_EX
@ -98,5 +103,7 @@ namespace skyline::service::nvdrv::device::nvhost {
PosixResult Remap(span<RemapEntry> entries);
PosixResult Ioctl(IoctlDescriptor cmd, span<u8> buffer) override;
PosixResult Ioctl3(IoctlDescriptor cmd, span<u8> buffer, span<u8> inlineBuffer) override;
};
}

View File

@ -26,9 +26,22 @@ namespace skyline::service::nvdrv::device::nvhost {
return PosixResult::Success;
}
PosixResult CtrlGpu::GetTpcMasks(In<u32> bufSize, Out<u64> maskBuf) {
// TODO
maskBuf = 0x3;
PosixResult CtrlGpu::GetCharacteristics3(span<u8> inlineBuffer, InOut<u64> size, In<u64> userAddress, Out<GpuCharacteristics> characteristics) {
inlineBuffer.as<GpuCharacteristics>() = {};
return GetCharacteristics(size, userAddress, characteristics);
}
PosixResult CtrlGpu::GetTpcMasks(In<u32> bufSize, Out<u32> mask) {
if (bufSize)
mask = 0x3;
return PosixResult::Success;
}
PosixResult CtrlGpu::GetTpcMasks3(span<u8> inlineBuffer, In<u32> bufSize, Out<u32> mask) {
if (bufSize)
mask = inlineBuffer.as<u32>() = 0x3;
return PosixResult::Success;
}
@ -60,7 +73,7 @@ namespace skyline::service::nvdrv::device::nvhost {
IOCTL_CASE_ARGS(INOUT, SIZE(0xB0), MAGIC(CtrlGpuMagic), FUNC(0x5),
GetCharacteristics, ARGS(InOut<u64>, In<u64>, Out<GpuCharacteristics>))
IOCTL_CASE_ARGS(INOUT, SIZE(0x18), MAGIC(CtrlGpuMagic), FUNC(0x6),
GetTpcMasks, ARGS(In<u32>, Pad<u32, 3>, Out<u64>))
GetTpcMasks, ARGS(In<u32>, Pad<u32, 3>, Out<u32>))
IOCTL_CASE_ARGS(OUT, SIZE(0x8), MAGIC(CtrlGpuMagic), FUNC(0x14),
GetActiveSlotMask, ARGS(Out<u32>, Out<u32>))
}))

View File

@ -93,11 +93,21 @@ namespace skyline::service::nvdrv::device::nvhost {
*/
PosixResult GetCharacteristics(InOut<u64> size, In<u64> userAddress, Out<GpuCharacteristics> characteristics);
/**
* @brief Ioctl3 variant of GetTpcMasks
*/
PosixResult GetCharacteristics3(span<u8> inlineBuffer, InOut<u64> size, In<u64> userAddress, Out<GpuCharacteristics> characteristics);
/**
* @brief Returns the TPC mask value for each GPC
* @url https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_GET_TPC_MASKS
*/
PosixResult GetTpcMasks(In<u32> bufSize, Out<u64> maskBuf);
PosixResult GetTpcMasks(In<u32> bufSize, Out<u32> mask);
/**
* @brief Ioctl3 variant of GetTpcMasks
*/
PosixResult GetTpcMasks3(span<u8> inlineBuffer, In<u32> bufSize, Out<u32> mask);
/**
* @brief Returns the mask value for a ZBC slot

View File

@ -23,7 +23,7 @@ namespace skyline::service::nvdrv::device::nvhost {
}
PosixResult GpuChannel::SubmitGpfifo(In<u64> userAddress, In<u32> numEntries, InOut<SubmitGpfifoFlags> flags, InOut<Fence> fence, span<soc::gm20b::GpEntry> gpEntries) {
if (numEntries != gpEntries.size())
if (numEntries > gpEntries.size())
throw exception("GpEntry size mismatch!");
if (flags.fenceWait) {
@ -34,7 +34,7 @@ namespace skyline::service::nvdrv::device::nvhost {
throw exception("Waiting on a fence through SubmitGpfifo is unimplemented");
}
state.soc->gm20b.gpfifo.Push(gpEntries);
state.soc->gm20b.gpfifo.Push(gpEntries.subspan(0, numEntries));
fence.id = channelSyncpoint;
@ -49,6 +49,10 @@ namespace skyline::service::nvdrv::device::nvhost {
return PosixResult::Success;
}
PosixResult GpuChannel::SubmitGpfifo2(span<u8> inlineBuffer, In<u64> userAddress, In<u32> numEntries, InOut<GpuChannel::SubmitGpfifoFlags> flags, InOut<Fence> fence) {
return SubmitGpfifo(userAddress, numEntries, flags, fence, inlineBuffer.cast<soc::gm20b::GpEntry>());
}
PosixResult GpuChannel::AllocObjCtx(In<u32> classId, In<u32> flags, Out<u64> objId) {
return PosixResult::Success;
}
@ -129,5 +133,10 @@ namespace skyline::service::nvdrv::device::nvhost {
VARIABLE_IOCTL_CASE_ARGS(INOUT, MAGIC(GpuChannelMagic), FUNC(0x8),
SubmitGpfifo, ARGS(In<u64>, In<u32>, InOut<SubmitGpfifoFlags>, InOut<Fence>, AutoSizeSpan<soc::gm20b::GpEntry>))
}))
INLINE_IOCTL_HANDLER_FUNC(Ioctl2, GpuChannel, ({
INLINE_IOCTL_CASE_ARGS(INOUT, SIZE(0x18), MAGIC(GpuChannelMagic), FUNC(0x1B),
SubmitGpfifo2, ARGS(In<u64>, In<u32>, InOut<SubmitGpfifoFlags>, InOut<Fence>))
}))
#include <services/nvdrv/devices/deserialisation/macro_undef.h>
}

View File

@ -57,6 +57,11 @@ namespace skyline::service::nvdrv::device::nvhost {
*/
PosixResult SubmitGpfifo(In<u64> userAddress, In<u32> numEntries, InOut<SubmitGpfifoFlags> flags, InOut<Fence> fence, span<soc::gm20b::GpEntry> gpEntries);
/**
* @brief Ioctl2 variant of SubmitGpfifo
*/
PosixResult SubmitGpfifo2(span<u8> inlineBuffer, In<u64> userAddress, In<u32> numEntries, InOut<SubmitGpfifoFlags> flags, InOut<Fence> fence);
/**
* @brief Allocates a graphic context object
* @url https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_ALLOC_OBJ_CTX
@ -108,5 +113,7 @@ namespace skyline::service::nvdrv::device::nvhost {
std::shared_ptr<type::KEvent> QueryEvent(u32 eventId) override;
PosixResult Ioctl(IoctlDescriptor cmd, span<u8> buffer) override;
PosixResult Ioctl2(IoctlDescriptor cmd, span<u8> buffer, span<u8> inlineBuffer) override;
};
}

View File

@ -13,6 +13,7 @@ namespace skyline::service::nvdrv::device {
if (h) {
(*h)->origSize = size; // Orig size is the unaligned size
handle = (*h)->id;
state.logger->Debug("handle: {}, size: 0x{:X}", (*h)->id, size);
}
return h;
@ -47,6 +48,8 @@ namespace skyline::service::nvdrv::device {
if (!h) [[unlikely]]
return PosixResult::InvalidArgument;
state.logger->Debug("handle: {}, flags: ( mapUncached: {}, keepUncachedAfterFree: {} ), align: 0x{:X}, kind: {}, address: 0x{:X}", handle, flags.mapUncached, flags.keepUncachedAfterFree, align, kind, address);
return h->Alloc(flags, align, kind, address);
}

View File

@ -95,11 +95,34 @@ namespace skyline::service::nvdrv {
}
}
NvResult Driver::Ioctl(u32 fd, IoctlDescriptor cmd, span<u8> buffer) {
state.logger->Debug("fd: {}, cmd: 0x{:X}, device: {}", fd, cmd.raw, devices.at(fd)->GetName());
try {
return ConvertResult(devices.at(fd)->Ioctl(cmd, buffer));
} catch (const std::out_of_range &) {
throw exception("GetDevice was called with invalid file descriptor: 0x{:X}", fd);
throw exception("Ioctl was called with invalid file descriptor: 0x{:X}", fd);
}
}
NvResult Driver::Ioctl2(u32 fd, IoctlDescriptor cmd, span<u8> buffer, span<u8> inlineBuffer) {
state.logger->Debug("fd: {}, cmd: 0x{:X}, device: {}", fd, cmd.raw, devices.at(fd)->GetName());
try {
return ConvertResult(devices.at(fd)->Ioctl2(cmd, buffer, inlineBuffer));
} catch (const std::out_of_range &) {
throw exception("Ioctl2 was called with invalid file descriptor: 0x{:X}", fd);
}
}
NvResult Driver::Ioctl3(u32 fd, IoctlDescriptor cmd, span<u8> buffer, span<u8> inlineBuffer) {
state.logger->Debug("fd: {}, cmd: 0x{:X}, device: {}", fd, cmd.raw, devices.at(fd)->GetName());
try {
return ConvertResult(devices.at(fd)->Ioctl3(cmd, buffer, inlineBuffer));
} catch (const std::out_of_range &) {
throw exception("Ioctl3 was called with invalid file descriptor: 0x{:X}", fd);
}
}
@ -112,6 +135,8 @@ namespace skyline::service::nvdrv {
}
std::shared_ptr<kernel::type::KEvent> Driver::QueryEvent(u32 fd, u32 eventId) {
state.logger->Debug("fd: {}, eventId: 0x{:X}, device: {}", fd, eventId, devices.at(fd)->GetName());
try {
return devices.at(fd)->QueryEvent(eventId);
} catch (const std::exception &) {

View File

@ -32,6 +32,16 @@ namespace skyline::service::nvdrv {
*/
NvResult Ioctl(u32 fd, IoctlDescriptor cmd, span<u8> buffer);
/**
* @brief Calls an IOCTL on the device specified by `fd` using the given inline input buffer
*/
NvResult Ioctl2(u32 fd, IoctlDescriptor cmd, span<u8> buffer, span<u8> inlineBuffer);
/**
* @brief Calls an IOCTL on the device specified by `fd` using the given inline output buffer
*/
NvResult Ioctl3(u32 fd, IoctlDescriptor cmd, span<u8> buffer, span<u8> inlineBuffer);
/**
* @brief Queries a KEvent for the given `eventId` for the device specified by `fd`
*/

View File

@ -90,4 +90,7 @@ namespace skyline::service::nvdrv {
AccessDenied = 0x30010,
IoctlFailed = 0x3000F
};
template<typename ValueType>
using NvResultValue = ResultValue<ValueType, NvResult>;
}