nvdrv: add ioctl command serialization, convert nvhost_as_gpu

This commit is contained in:
Liam 2023-10-24 10:28:03 -04:00
parent 008d7e8c5f
commit 6256e3ca8e
5 changed files with 152 additions and 71 deletions

View file

@ -0,0 +1,107 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include <vector>
#include "common/concepts.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
namespace Service::Nvidia::Devices {
struct Ioctl1Traits {
template <typename T, typename R, typename A>
static T GetClassImpl(R (T::*)(A));
template <typename T, typename R, typename A>
static A GetArgImpl(R (T::*)(A));
};
struct Ioctl23Traits {
template <typename T, typename R, typename A, typename B>
static T GetClassImpl(R (T::*)(A, B));
template <typename T, typename R, typename A, typename B>
static A GetArgImpl(R (T::*)(A, B));
};
template <typename T>
struct ContainerType {
using ValueType = T;
};
template <Common::IsContiguousContainer T>
struct ContainerType<T> {
using ValueType = T::value_type;
};
template <typename InnerArg, typename F, typename Self, typename... Rest>
NvResult Wrap(std::span<const u8> input, std::span<u8> output, Self* self, F&& callable,
Rest&&... rest) {
using Arg = ContainerType<InnerArg>::ValueType;
constexpr bool ArgumentIsContainer = Common::IsContiguousContainer<InnerArg>;
// Verify that the input and output sizes are valid.
const size_t in_params = input.size() / sizeof(Arg);
const size_t out_params = output.size() / sizeof(Arg);
if (in_params * sizeof(Arg) != input.size()) {
return NvResult::InvalidSize;
}
if (out_params * sizeof(Arg) != output.size()) {
return NvResult::InvalidSize;
}
if (in_params == 0 && out_params == 0 && !ArgumentIsContainer) {
return NvResult::InvalidSize;
}
// Copy inputs, if needed.
std::vector<Arg> params(std::max(in_params, out_params));
if (in_params > 0) {
std::memcpy(params.data(), input.data(), input.size());
}
// Perform the call.
NvResult result;
if constexpr (ArgumentIsContainer) {
result = (self->*callable)(params, std::forward<Rest>(rest)...);
} else {
result = (self->*callable)(params.front(), std::forward<Rest>(rest)...);
}
// Copy outputs, if needed.
if (out_params > 0) {
std::memcpy(output.data(), params.data(), output.size());
}
return result;
}
template <typename F>
NvResult nvdevice::Wrap1(F&& callable, std::span<const u8> input, std::span<u8> output) {
using Self = decltype(Ioctl1Traits::GetClassImpl(callable));
using InnerArg = std::remove_reference_t<decltype(Ioctl1Traits::GetArgImpl(callable))>;
return Wrap<InnerArg>(input, output, static_cast<Self*>(this), callable);
}
template <typename F>
NvResult nvdevice::Wrap2(F&& callable, std::span<const u8> input, std::span<const u8> inline_input,
std::span<u8> output) {
using Self = decltype(Ioctl23Traits::GetClassImpl(callable));
using InnerArg = std::remove_reference_t<decltype(Ioctl23Traits::GetArgImpl(callable))>;
return Wrap<InnerArg>(input, output, static_cast<Self*>(this), callable, inline_input);
}
template <typename F>
NvResult nvdevice::Wrap3(F&& callable, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) {
using Self = decltype(Ioctl23Traits::GetClassImpl(callable));
using InnerArg = std::remove_reference_t<decltype(Ioctl23Traits::GetArgImpl(callable))>;
return Wrap<InnerArg>(input, output, static_cast<Self*>(this), callable, inline_output);
}
} // namespace Service::Nvidia::Devices

View file

@ -74,6 +74,18 @@ public:
return nullptr; return nullptr;
} }
protected:
template <typename F>
NvResult Wrap1(F&& callable, std::span<const u8> input, std::span<u8> output);
template <typename F>
NvResult Wrap2(F&& callable, std::span<const u8> input, std::span<const u8> inline_input,
std::span<u8> output);
template <typename F>
NvResult Wrap3(F&& callable, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output);
protected: protected:
Core::System& system; Core::System& system;
}; };

View file

@ -11,6 +11,7 @@
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
#include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/nvdrv.h"
@ -33,21 +34,21 @@ NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> i
case 'A': case 'A':
switch (command.cmd) { switch (command.cmd) {
case 0x1: case 0x1:
return BindChannel(input, output); return Wrap1(&nvhost_as_gpu::BindChannel, input, output);
case 0x2: case 0x2:
return AllocateSpace(input, output); return Wrap1(&nvhost_as_gpu::AllocateSpace, input, output);
case 0x3: case 0x3:
return FreeSpace(input, output); return Wrap1(&nvhost_as_gpu::FreeSpace, input, output);
case 0x5: case 0x5:
return UnmapBuffer(input, output); return Wrap1(&nvhost_as_gpu::UnmapBuffer, input, output);
case 0x6: case 0x6:
return MapBufferEx(input, output); return Wrap1(&nvhost_as_gpu::MapBufferEx, input, output);
case 0x8: case 0x8:
return GetVARegions(input, output); return Wrap1(&nvhost_as_gpu::GetVARegions1, input, output);
case 0x9: case 0x9:
return AllocAsEx(input, output); return Wrap1(&nvhost_as_gpu::AllocAsEx, input, output);
case 0x14: case 0x14:
return Remap(input, output); return Wrap1(&nvhost_as_gpu::Remap, input, output);
default: default:
break; break;
} }
@ -72,7 +73,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i
case 'A': case 'A':
switch (command.cmd) { switch (command.cmd) {
case 0x8: case 0x8:
return GetVARegions(input, output, inline_output); return Wrap3(&nvhost_as_gpu::GetVARegions3, input, output, inline_output);
default: default:
break; break;
} }
@ -87,10 +88,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i
void nvhost_as_gpu::OnOpen(DeviceFD fd) {} void nvhost_as_gpu::OnOpen(DeviceFD fd) {}
void nvhost_as_gpu::OnClose(DeviceFD fd) {} void nvhost_as_gpu::OnClose(DeviceFD fd) {}
NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
IoctlAllocAsEx params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size); LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size);
std::scoped_lock lock(mutex); std::scoped_lock lock(mutex);
@ -141,10 +139,7 @@ NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::span<u8> outpu
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_as_gpu::AllocateSpace(IoctlAllocSpace& params) {
IoctlAllocSpace params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages,
params.page_size, params.flags); params.page_size, params.flags);
@ -194,7 +189,6 @@ NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::span<u8> o
.big_pages = params.page_size != VM::YUZU_PAGESIZE, .big_pages = params.page_size != VM::YUZU_PAGESIZE,
}; };
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
@ -222,10 +216,7 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
mapping_map.erase(offset); mapping_map.erase(offset);
} }
NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) {
IoctlFreeSpace params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset,
params.pages, params.page_size); params.pages, params.page_size);
@ -264,18 +255,11 @@ NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::span<u8> outpu
return NvResult::BadValue; return NvResult::BadValue;
} }
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) {
const auto num_entries = input.size() / sizeof(IoctlRemapEntry); LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", entries.size());
LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
std::scoped_lock lock(mutex);
entries.resize_destructive(num_entries);
std::memcpy(entries.data(), input.data(), input.size());
if (!vm.initialised) { if (!vm.initialised) {
return NvResult::BadValue; return NvResult::BadValue;
@ -317,14 +301,10 @@ NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::span<u8> output) {
} }
} }
std::memcpy(output.data(), entries.data(), output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
IoctlMapBufferEx params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, LOG_DEBUG(Service_NVDRV,
"called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}" "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}"
", offset={}", ", offset={}",
@ -421,14 +401,10 @@ NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::span<u8> out
mapping_map[params.offset] = mapping; mapping_map[params.offset] = mapping;
} }
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) {
IoctlUnmapBuffer params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
std::scoped_lock lock(mutex); std::scoped_lock lock(mutex);
@ -464,9 +440,7 @@ NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::span<u8> out
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_as_gpu::BindChannel(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_as_gpu::BindChannel(IoctlBindChannel& params) {
IoctlBindChannel params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd); auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd);
@ -493,10 +467,7 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) {
}; };
} }
NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_as_gpu::GetVARegions1(IoctlGetVaRegions& params) {
IoctlGetVaRegions params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
params.buf_size); params.buf_size);
@ -508,15 +479,10 @@ NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> ou
GetVARegionsImpl(params); GetVARegionsImpl(params);
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output, NvResult nvhost_as_gpu::GetVARegions3(IoctlGetVaRegions& params, std::span<u8> inline_output) {
std::span<u8> inline_output) {
IoctlGetVaRegions params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
params.buf_size); params.buf_size);
@ -528,9 +494,7 @@ NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> ou
GetVARegionsImpl(params); GetVARegionsImpl(params);
std::memcpy(output.data(), &params, output.size()); std::memcpy(inline_output.data(), params.regions.data(), 2 * sizeof(VaRegion));
std::memcpy(inline_output.data(), &params.regions[0], sizeof(VaRegion));
std::memcpy(inline_output.data() + sizeof(VaRegion), &params.regions[1], sizeof(VaRegion));
return NvResult::Success; return NvResult::Success;
} }

View file

@ -139,18 +139,17 @@ private:
static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2, static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2,
"IoctlGetVaRegions is incorrect size"); "IoctlGetVaRegions is incorrect size");
NvResult AllocAsEx(std::span<const u8> input, std::span<u8> output); NvResult AllocAsEx(IoctlAllocAsEx& params);
NvResult AllocateSpace(std::span<const u8> input, std::span<u8> output); NvResult AllocateSpace(IoctlAllocSpace& params);
NvResult Remap(std::span<const u8> input, std::span<u8> output); NvResult Remap(std::span<IoctlRemapEntry> params);
NvResult MapBufferEx(std::span<const u8> input, std::span<u8> output); NvResult MapBufferEx(IoctlMapBufferEx& params);
NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output); NvResult UnmapBuffer(IoctlUnmapBuffer& params);
NvResult FreeSpace(std::span<const u8> input, std::span<u8> output); NvResult FreeSpace(IoctlFreeSpace& params);
NvResult BindChannel(std::span<const u8> input, std::span<u8> output); NvResult BindChannel(IoctlBindChannel& params);
void GetVARegionsImpl(IoctlGetVaRegions& params); void GetVARegionsImpl(IoctlGetVaRegions& params);
NvResult GetVARegions(std::span<const u8> input, std::span<u8> output); NvResult GetVARegions1(IoctlGetVaRegions& params);
NvResult GetVARegions(std::span<const u8> input, std::span<u8> output, NvResult GetVARegions3(IoctlGetVaRegions& params, std::span<u8> inline_output);
std::span<u8> inline_output);
void FreeMappingLocked(u64 offset); void FreeMappingLocked(u64 offset);
@ -213,7 +212,6 @@ private:
bool initialised{}; bool initialised{};
} vm; } vm;
std::shared_ptr<Tegra::MemoryManager> gmmu; std::shared_ptr<Tegra::MemoryManager> gmmu;
Common::ScratchBuffer<IoctlRemapEntry> entries;
// s32 channel{}; // s32 channel{};
// u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE}; // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE};

View file

@ -134,7 +134,7 @@ NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::span<u8> outp
LOG_DEBUG(Service_NVDRV, "called"); LOG_DEBUG(Service_NVDRV, "called");
IoctlClientData params{}; IoctlClientData params{};
std::memcpy(&params, input.data(), input.size()); std::memcpy(&params, input.data(), std::min(sizeof(IoctlClientData), input.size()));
user_data = params.data; user_data = params.data;
return NvResult::Success; return NvResult::Success;
} }
@ -143,9 +143,9 @@ NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::span<u8> outp
LOG_DEBUG(Service_NVDRV, "called"); LOG_DEBUG(Service_NVDRV, "called");
IoctlClientData params{}; IoctlClientData params{};
std::memcpy(&params, input.data(), input.size()); std::memcpy(&params, input.data(), std::min(sizeof(IoctlClientData), input.size()));
params.data = user_data; params.data = user_data;
std::memcpy(output.data(), &params, output.size()); std::memcpy(output.data(), &params, std::min(sizeof(IoctlClientData), output.size()));
return NvResult::Success; return NvResult::Success;
} }