diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/as_gpu.cpp b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/as_gpu.cpp new file mode 100644 index 00000000..01701f41 --- /dev/null +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/as_gpu.cpp @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: MIT OR MPL-2.0 +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include +#include +#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 channelFd) { + // TODO: support once multiple address spaces are supported + return PosixResult::Success; + } + + PosixResult AsGpu::AllocSpace(In pages, In pageSize, In flags, InOut offset) { + // TODO: track this on the nvdrv side and have the gmmu only do virt -> phys + // Also fix error codes + u64 size{static_cast(pages) * static_cast(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 offset, In pages, In pageSize) { + // TODO: implement this when we add nvdrv side address space allocation + return PosixResult::Success; + } + + PosixResult AsGpu::UnmapBuffer(In 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 flags, In kind, In handle, InOut pageSize, In bufferOffset, In mappingSize, InOut 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(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 bufAddr, InOut bufSize, Out> vaRegions) { + // TODO: impl when we move allocator to nvdrv + return PosixResult::Success; + } + + PosixResult AsGpu::AllocAsEx(In bigPageSize, In asFd, In flags, In vaRangeStart, In vaRangeEnd, In vaRangeSplit) { + // TODO: create the allocator here + return PosixResult::Success; + } + + PosixResult AsGpu::Remap(span 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(entry.asOffsetBigPages) << BigPageSize}; + u8 *cpuPtr{reinterpret_cast(h->address + (static_cast(entry.handleOffsetBigPages) << BigPageSize))}; + u64 size{static_cast(entry.bigPages) << BigPageSize}; + + state.soc->gmmu.MapFixed(virtAddr, cpuPtr, size); + } + + return PosixResult::Success; + } + +#include + static constexpr u32 AsGpuMagic{0x41}; + + VARIABLE_IOCTL_HANDLER_FUNC(AsGpu, ({ + IOCTL_CASE_ARGS(IN, SIZE(0x4), MAGIC(AsGpuMagic), FUNC(0x1), + BindChannel, ARGS(In)) + IOCTL_CASE_ARGS(INOUT, SIZE(0x18), MAGIC(AsGpuMagic), FUNC(0x2), + AllocSpace, ARGS(In, In, In, Pad, InOut)) + IOCTL_CASE_ARGS(INOUT, SIZE(0x10), MAGIC(AsGpuMagic), FUNC(0x3), + FreeSpace, ARGS(In, In, In)) + IOCTL_CASE_ARGS(INOUT, SIZE(0x8), MAGIC(AsGpuMagic), FUNC(0x5), + UnmapBuffer, ARGS(In)) + IOCTL_CASE_ARGS(INOUT, SIZE(0x28), MAGIC(AsGpuMagic), FUNC(0x6), + MapBufferEx, ARGS(In, In, In, InOut, In, In, InOut)) + IOCTL_CASE_ARGS(INOUT, SIZE(0x40), MAGIC(AsGpuMagic), FUNC(0x8), + GetVaRegions, ARGS(In, InOut, Pad, Out>)) + IOCTL_CASE_ARGS(IN, SIZE(0x28), MAGIC(AsGpuMagic), FUNC(0x9), + AllocAsEx, ARGS(In, In, In, Pad, In, In, In)) + }), ({ + VARIABLE_IOCTL_CASE_ARGS(INOUT, MAGIC(AsGpuMagic), FUNC(0x14), + Remap, ARGS(AutoSizeSpan)) + })) +#include +} diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/as_gpu.h b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/as_gpu.h new file mode 100644 index 00000000..9dbad2af --- /dev/null +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/as_gpu.h @@ -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 + +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 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 channelFd); + + /** + * @brief Reserves a region in this address space + * @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_FREE_SPACE + */ + PosixResult AllocSpace(In pages, In pageSize, In flags, InOut 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 offset, In pages, In pageSize); + + /** + * @brief Unmaps a region in this address space + * @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_UNMAP_BUFFER + */ + PosixResult UnmapBuffer(In 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 flags, In kind, In handle, InOut pageSize, In bufferOffset, In mappingSize, InOut 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 bufAddr, InOut bufSize, Out> 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 bigPageSize, In asFd, In flags, In vaRangeStart, In vaRangeEnd, In vaRangeSplit); + + /** + * @brief Remaps a region of the GPU address space + * @url https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_REMAP + */ + PosixResult Remap(span entries); + + PosixResult Ioctl(IoctlDescriptor cmd, span buffer) override; + }; +} diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp deleted file mode 100644 index 354e1467..00000000 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp +++ /dev/null @@ -1,196 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) - -#include -#include -#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 buffer, span inlineBuffer) { - return NvStatus::Success; - } - - NvStatus NvHostAsGpu::AllocSpace(IoctlType type, span buffer, span inlineBuffer) { - struct Data { - u32 pages; // In - u32 pageSize; // In - MappingFlags flags; // In - u32 _pad_; - union { - u64 offset; // InOut - u64 align; // In - }; - } ®ion = buffer.as(); - - u64 size{static_cast(region.pages) * static_cast(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 buffer, span inlineBuffer) { - u64 offset{buffer.as()}; - - 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 buffer, span 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(); - - 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 buffer, span inlineBuffer) { - /* - struct Data { - u64 _pad0_; - u32 bufferSize; // InOut - u32 _pad1_; - - struct { - u64 offset; - u32 page_size; - u32 pad; - u64 pages; - } regions[2]; // Out - } ®ionInfo = buffer.as(); - */ - return NvStatus::Success; - } - - NvStatus NvHostAsGpu::AllocAsEx(IoctlType type, span buffer, span 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(); - */ - return NvStatus::Success; - } - - NvStatus NvHostAsGpu::Remap(IoctlType type, span buffer, span 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()}; - 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(entry.gpuOffset) << MinAlignmentShift}; - u8 *cpuPtr{mapping->ptr + (static_cast(entry.mapOffset) << MinAlignmentShift)}; - u64 size{static_cast(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; - } -} diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.h b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.h deleted file mode 100644 index a778e833..00000000 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.h +++ /dev/null @@ -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 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 buffer, span 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 buffer, span 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 buffer, span 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 buffer, span 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 buffer, span 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 buffer, span 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 buffer, span 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) - ) - }; -}