Move nvhost-as-gpu to new device API

This commit is contained in:
Billy Laws 2021-07-17 17:46:47 +01:00
parent 054a7aa91e
commit a513f84fff
4 changed files with 258 additions and 274 deletions

View File

@ -0,0 +1,156 @@
// SPDX-License-Identifier: MIT OR MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <soc.h>
#include <services/nvdrv/devices/deserialisation/deserialisation.h>
#include "as_gpu.h"
namespace skyline::service::nvdrv::device::nvhost {
AsGpu::AsGpu(const DeviceState &state, Core &core, const SessionContext &ctx) : NvDevice(state, core, ctx) {}
PosixResult AsGpu::BindChannel(In<FileDescriptor> channelFd) {
// TODO: support once multiple address spaces are supported
return PosixResult::Success;
}
PosixResult AsGpu::AllocSpace(In<u32> pages, In<u32> pageSize, In<MappingFlags> flags, InOut<u64> offset) {
// TODO: track this on the nvdrv side and have the gmmu only do virt -> phys
// Also fix error codes
u64 size{static_cast<u64>(pages) * static_cast<u64>(pageSize)};
if (flags.fixed)
offset = state.soc->gmmu.ReserveFixed(offset, size);
else
offset = state.soc->gmmu.ReserveSpace(size, offset); // offset contains the input alignment
if (offset == 0) {
state.logger->Warn("Failed to allocate GPU address space region!");
return PosixResult::InvalidArgument;
}
return PosixResult::Success;
}
PosixResult AsGpu::FreeSpace(In<u64> offset, In<u32> pages, In<u32> pageSize) {
// TODO: implement this when we add nvdrv side address space allocation
return PosixResult::Success;
}
PosixResult AsGpu::UnmapBuffer(In<u64> offset) {
try {
auto region{regionMap.at(offset)};
// Non-fixed regions are unmapped so that they can be used by future non-fixed mappings
if (!region.fixed)
if (!state.soc->gmmu.Unmap(offset, region.size))
state.logger->Warn("Failed to unmap region at 0x{:X}", offset);
regionMap.erase(offset);
} catch (const std::out_of_range &e) {
state.logger->Warn("Couldn't find region to unmap at 0x{:X}", offset);
}
return PosixResult::Success;
}
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) {
if (flags.remap) {
auto region{regionMap.lower_bound(offset)};
if (region == regionMap.end()) {
state.logger->Warn("Cannot remap an unmapped GPU address space region: 0x{:X}", offset);
return PosixResult::InvalidArgument;
}
if (region->second.size < mappingSize) {
state.logger->Warn("Cannot remap an partially mapped GPU address space region: 0x{:X}", offset);
return PosixResult::InvalidArgument;
}
u64 gpuAddress{offset + bufferOffset};
u8 *cpuPtr{region->second.ptr + bufferOffset};
if (!state.soc->gmmu.MapFixed(gpuAddress, cpuPtr, mappingSize)) {
state.logger->Warn("Failed to remap GPU address space region: 0x{:X}", gpuAddress);
return PosixResult::InvalidArgument;
}
return PosixResult::Success;
}
auto h{core.nvMap.GetHandle(handle)};
if (!h)
return PosixResult::InvalidArgument;
if (auto err{h->Duplicate(ctx.internalSession)}; err != PosixResult::Success)
return err;
u8 *cpuPtr{reinterpret_cast<u8 *>(h->address + bufferOffset)};
u64 size{mappingSize ? mappingSize : h->origSize};
if (flags.fixed)
offset = state.soc->gmmu.MapFixed(offset, cpuPtr, size);
else
offset = state.soc->gmmu.MapAllocate(cpuPtr, size);
if (offset == 0) {
state.logger->Warn("Failed to map GPU address space region!");
return PosixResult::InvalidArgument;
}
regionMap[offset] = {cpuPtr, size, flags.fixed};
return PosixResult::Success;
}
PosixResult AsGpu::GetVaRegions(In<u64> bufAddr, InOut<u32> bufSize, Out<std::array<VaRegion, 2>> vaRegions) {
// TODO: impl when we move allocator to nvdrv
return PosixResult::Success;
}
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;
}
PosixResult AsGpu::Remap(span<RemapEntry> entries) {
constexpr u32 BigPageSize{0x10}; //!< The big page size of the GPU
for (const auto &entry : entries) {
auto h{core.nvMap.GetHandle(entry.handle)};
if (!h)
return PosixResult::InvalidArgument;
u64 virtAddr{static_cast<u64>(entry.asOffsetBigPages) << BigPageSize};
u8 *cpuPtr{reinterpret_cast<u8 *>(h->address + (static_cast<u64>(entry.handleOffsetBigPages) << BigPageSize))};
u64 size{static_cast<u64>(entry.bigPages) << BigPageSize};
state.soc->gmmu.MapFixed(virtAddr, cpuPtr, size);
}
return PosixResult::Success;
}
#include <services/nvdrv/devices/deserialisation/macro_def.h>
static constexpr u32 AsGpuMagic{0x41};
VARIABLE_IOCTL_HANDLER_FUNC(AsGpu, ({
IOCTL_CASE_ARGS(IN, SIZE(0x4), MAGIC(AsGpuMagic), FUNC(0x1),
BindChannel, ARGS(In<FileDescriptor>))
IOCTL_CASE_ARGS(INOUT, SIZE(0x18), MAGIC(AsGpuMagic), FUNC(0x2),
AllocSpace, ARGS(In<u32>, In<u32>, In<MappingFlags>, Pad<u32>, InOut<u64>))
IOCTL_CASE_ARGS(INOUT, SIZE(0x10), MAGIC(AsGpuMagic), FUNC(0x3),
FreeSpace, ARGS(In<u64>, In<u32>, In<u32>))
IOCTL_CASE_ARGS(INOUT, SIZE(0x8), MAGIC(AsGpuMagic), FUNC(0x5),
UnmapBuffer, ARGS(In<u64>))
IOCTL_CASE_ARGS(INOUT, SIZE(0x28), MAGIC(AsGpuMagic), FUNC(0x6),
MapBufferEx, ARGS(In<MappingFlags>, In<u32>, In<core::NvMap::Handle::Id>, InOut<u32>, In<u64>, In<u64>, InOut<u64>))
IOCTL_CASE_ARGS(INOUT, SIZE(0x40), MAGIC(AsGpuMagic), FUNC(0x8),
GetVaRegions, ARGS(In<u64>, InOut<u32>, Pad<u32>, Out<std::array<VaRegion, 2>>))
IOCTL_CASE_ARGS(IN, SIZE(0x28), MAGIC(AsGpuMagic), FUNC(0x9),
AllocAsEx, ARGS(In<u32>, In<FileDescriptor>, In<u32>, Pad<u32>, In<u64>, In<u64>, In<u64>))
}), ({
VARIABLE_IOCTL_CASE_ARGS(INOUT, MAGIC(AsGpuMagic), FUNC(0x14),
Remap, ARGS(AutoSizeSpan<RemapEntry>))
}))
#include <services/nvdrv/devices/deserialisation/macro_undef.h>
}

View File

@ -0,0 +1,102 @@
// SPDX-License-Identifier: MIT OR MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <services/nvdrv/devices/nvdevice.h>
namespace skyline::service::nvdrv::device::nvhost {
/**
* @brief nvhost::AsGpu (/dev/nvhost-as-gpu) is used to access a GPU virtual address space
* @url https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-as-gpu
*/
class AsGpu : public NvDevice {
private:
struct AddressSpaceRegion {
u8 *ptr;
u64 size;
bool fixed;
};
std::map<u64, AddressSpaceRegion> regionMap; //!< This maps the base addresses of mapped buffers to their total sizes and mapping type, this is needed as what was originally a single buffer may have been split into multiple GPU side buffers with the remap flag.
public:
struct MappingFlags {
bool fixed : 1;
u8 _pad0_ : 7;
bool remap : 1;
u32 _pad1_ : 23;
};
static_assert(sizeof(MappingFlags) == sizeof(u32));
struct VaRegion {
u64 offset;
u32 pageSize;
u32 _pad0_;
u64 pages;
};
static_assert(sizeof(VaRegion) == 0x18);
struct RemapEntry {
u16 flags;
u16 kind;
core::NvMap::Handle::Id handle;
u32 handleOffsetBigPages;
u32 asOffsetBigPages;
u32 bigPages;
};
static_assert(sizeof(RemapEntry) == 0x14);
AsGpu(const DeviceState &state, Core &core, const SessionContext &ctx);
/**
* @brief Binds this address space to a channel
* @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_BIND_CHANNEL
*/
PosixResult BindChannel(In<FileDescriptor> channelFd);
/**
* @brief Reserves a region in this address space
* @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_FREE_SPACE
*/
PosixResult AllocSpace(In<u32> pages, In<u32> pageSize, In<MappingFlags> flags, InOut<u64> offset);
/**
* @brief Frees an allocated region in this address space
* @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_FREE_SPACE
*/
PosixResult FreeSpace(In<u64> offset, In<u32> pages, In<u32> pageSize);
/**
* @brief Unmaps a region in this address space
* @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_UNMAP_BUFFER
*/
PosixResult UnmapBuffer(In<u64> offset);
/**
* @brief Maps a region into this address space with extra parameters
* @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_MAP_BUFFER_EX
*/
PosixResult 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);
/**
* @brief Returns info about the address space and its page sizes
* @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_GET_VA_REGIONS
*/
PosixResult GetVaRegions(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
*/
PosixResult AllocAsEx(In<u32> bigPageSize, In<FileDescriptor> asFd, In<u32> flags, In<u64> vaRangeStart, In<u64> vaRangeEnd, In<u64> vaRangeSplit);
/**
* @brief Remaps a region of the GPU address space
* @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_REMAP
*/
PosixResult Remap(span<RemapEntry> entries);
PosixResult Ioctl(IoctlDescriptor cmd, span<u8> buffer) override;
};
}

View File

@ -1,196 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <soc.h>
#include <services/nvdrv/driver.h>
#include "nvmap.h"
#include "nvhost_as_gpu.h"
namespace skyline::service::nvdrv::device {
struct MappingFlags {
bool fixed : 1;
u8 _pad0_ : 7;
bool remap : 1;
u32 _pad1_ : 23;
};
static_assert(sizeof(MappingFlags) == sizeof(u32));
NvHostAsGpu::NvHostAsGpu(const DeviceState &state) : NvDevice(state) {}
NvStatus NvHostAsGpu::BindChannel(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
return NvStatus::Success;
}
NvStatus NvHostAsGpu::AllocSpace(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
struct Data {
u32 pages; // In
u32 pageSize; // In
MappingFlags flags; // In
u32 _pad_;
union {
u64 offset; // InOut
u64 align; // In
};
} &region = buffer.as<Data>();
u64 size{static_cast<u64>(region.pages) * static_cast<u64>(region.pageSize)};
if (region.flags.fixed)
region.offset = state.soc->gmmu.ReserveFixed(region.offset, size);
else
region.offset = state.soc->gmmu.ReserveSpace(size, region.align);
if (region.offset == 0) {
state.logger->Warn("Failed to allocate GPU address space region!");
return NvStatus::BadParameter;
}
return NvStatus::Success;
}
NvStatus NvHostAsGpu::UnmapBuffer(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
u64 offset{buffer.as<u64>()};
try {
auto region{regionMap.at(offset)};
// Non-fixed regions are unmapped so that they can be used by future non-fixed mappings
if (!region.fixed)
if (!state.soc->gmmu.Unmap(offset, region.size))
state.logger->Warn("Failed to unmap region at 0x{:X}", offset);
regionMap.erase(offset);
} catch (const std::out_of_range &e) {
state.logger->Warn("Couldn't find region to unmap at 0x{:X}", offset);
}
return NvStatus::Success;
}
NvStatus NvHostAsGpu::Modify(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
struct Data {
MappingFlags flags; // In
u32 kind; // In
u32 nvmapHandle; // In
u32 pageSize; // InOut
u64 bufferOffset; // In
u64 mappingSize; // In
u64 offset; // InOut
} &data = buffer.as<Data>();
try {
if (data.flags.remap) {
auto region{regionMap.lower_bound(data.offset)};
if (region == regionMap.end()) {
state.logger->Warn("Cannot remap an unmapped GPU address space region: 0x{:X}", data.offset);
return NvStatus::BadParameter;
}
if (region->second.size < data.mappingSize) {
state.logger->Warn("Cannot remap an partially mapped GPU address space region: 0x{:X}", data.offset);
return NvStatus::BadParameter;
}
u64 gpuAddress{data.offset + data.bufferOffset};
u8 *cpuPtr{region->second.ptr + data.bufferOffset};
if (!state.soc->gmmu.MapFixed(gpuAddress, cpuPtr, data.mappingSize)) {
state.logger->Warn("Failed to remap GPU address space region: 0x{:X}", gpuAddress);
return NvStatus::BadParameter;
}
return NvStatus::Success;
}
auto driver{nvdrv::driver.lock()};
auto nvmap{driver->nvMap.lock()};
auto mapping{nvmap->GetObject(data.nvmapHandle)};
u8 *cpuPtr{data.bufferOffset + mapping->ptr};
u64 size{data.mappingSize ? data.mappingSize : mapping->size};
if (data.flags.fixed)
data.offset = state.soc->gmmu.MapFixed(data.offset, cpuPtr, size);
else
data.offset = state.soc->gmmu.MapAllocate(cpuPtr, size);
if (data.offset == 0) {
state.logger->Warn("Failed to map GPU address space region!");
return NvStatus::BadParameter;
}
regionMap[data.offset] = {cpuPtr, size, data.flags.fixed};
return NvStatus::Success;
} catch (const std::out_of_range &) {
state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.nvmapHandle);
return NvStatus::BadParameter;
}
}
NvStatus NvHostAsGpu::GetVaRegions(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
/*
struct Data {
u64 _pad0_;
u32 bufferSize; // InOut
u32 _pad1_;
struct {
u64 offset;
u32 page_size;
u32 pad;
u64 pages;
} regions[2]; // Out
} &regionInfo = buffer.as<Data>();
*/
return NvStatus::Success;
}
NvStatus NvHostAsGpu::AllocAsEx(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
/*
struct Data {
u32 bigPageSize; // In
i32 asFd; // In
u32 flags; // In
u32 reserved; // In
u64 vaRangeStart; // In
u64 vaRangeEnd; // In
u64 vaRangeSplit; // In
} addressSpace = buffer.as<Data>();
*/
return NvStatus::Success;
}
NvStatus NvHostAsGpu::Remap(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
struct Entry {
u16 flags; // In
u16 kind; // In
u32 nvmapHandle; // In
u32 mapOffset; // In
u32 gpuOffset; // In
u32 pages; // In
};
constexpr u32 MinAlignmentShift{0x10}; // This shift is applied to all addresses passed to Remap
auto entries{buffer.cast<Entry>()};
for (const auto &entry : entries) {
try {
auto driver{nvdrv::driver.lock()};
auto nvmap{driver->nvMap.lock()};
auto mapping{nvmap->GetObject(entry.nvmapHandle)};
u64 virtAddr{static_cast<u64>(entry.gpuOffset) << MinAlignmentShift};
u8 *cpuPtr{mapping->ptr + (static_cast<u64>(entry.mapOffset) << MinAlignmentShift)};
u64 size{static_cast<u64>(entry.pages) << MinAlignmentShift};
state.soc->gmmu.MapFixed(virtAddr, cpuPtr, size);
} catch (const std::out_of_range &) {
state.logger->Warn("Invalid NvMap handle: 0x{:X}", entry.nvmapHandle);
return NvStatus::BadParameter;
}
}
return NvStatus::Success;
}
}

View File

@ -1,78 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include "nvdevice.h"
namespace skyline::service::nvdrv::device {
/**
* @brief NvHostAsGpu (/dev/nvhost-as-gpu) is used to access GPU virtual address spaces
* @url https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-as-gpu
*/
class NvHostAsGpu : public NvDevice {
private:
struct AddressSpaceRegion {
u8 *ptr;
u64 size;
bool fixed;
};
std::map<u64, AddressSpaceRegion> regionMap; //!< This maps the base addresses of mapped buffers to their total sizes and mapping type, this is needed as what was originally a single buffer may have been split into multiple GPU side buffers with the remap flag.
public:
NvHostAsGpu(const DeviceState &state);
/**
* @brief Binds a channel to the address space
* @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_BIND_CHANNEL
*/
NvStatus BindChannel(IoctlType type, span <u8> buffer, span <u8> inlineBuffer);
/**
* @brief Reserves a region in the GPU address space
* @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_ALLOC_SPACE
*/
NvStatus AllocSpace(IoctlType type, span <u8> buffer, span <u8> inlineBuffer);
/**
* @brief Unmaps a region in the GPU address space
* @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_UNMAP_BUFFER
*/
NvStatus UnmapBuffer(IoctlType type, span <u8> buffer, span <u8> inlineBuffer);
/**
* @brief Maps a region in the GPU address space
* @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_MODIFY
*/
NvStatus Modify(IoctlType type, span <u8> buffer, span <u8> inlineBuffer);
/**
* @brief Returns the application's GPU address space
* @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_GET_VA_REGIONS
*/
NvStatus GetVaRegions(IoctlType type, span <u8> buffer, span <u8> inlineBuffer);
/**
* @brief Initializes the application's GPU address space
* @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_ALLOC_AS_EX
*/
NvStatus AllocAsEx(IoctlType type, span <u8> buffer, span <u8> inlineBuffer);
/**
* @brief Remaps a region of the GPU address space
* @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_REMAP
*/
NvStatus Remap(IoctlType type, span <u8> buffer, span <u8> inlineBuffer);
NVDEVICE_DECL(
NVFUNC(0x4101, NvHostAsGpu, BindChannel),
NVFUNC(0x4102, NvHostAsGpu, AllocSpace),
NVFUNC(0x4105, NvHostAsGpu, UnmapBuffer),
NVFUNC(0x4106, NvHostAsGpu, Modify),
NVFUNC(0x4108, NvHostAsGpu, GetVaRegions),
NVFUNC(0x4109, NvHostAsGpu, AllocAsEx),
NVFUNC(0x4114, NvHostAsGpu, Remap)
)
};
}