From 72b5c448cfe5d21fea9dd7a996ca8b50539ec64a Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 20 May 2018 14:21:06 -0500 Subject: [PATCH 1/2] GPU: Implemented nvhost-as-gpu's UnmapBuffer ioctl. It removes a mapping previously created with the MapBufferEx ioctl. --- .../service/nvdrv/devices/nvhost_as_gpu.cpp | 33 +++++++++++++++++++ .../hle/service/nvdrv/devices/nvhost_as_gpu.h | 17 ++++++++++ src/video_core/memory_manager.cpp | 19 +++++++++++ src/video_core/memory_manager.h | 1 + 4 files changed, 70 insertions(+) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index cb4913b074..c1eea861df 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -26,6 +26,8 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector& input, std::vecto return BindChannel(input, output); case IoctlCommand::IocGetVaRegionsCommand: return GetVARegions(input, output); + case IoctlCommand::IocUnmapBufferCommand: + return UnmapBuffer(input, output); } if (static_cast(command.cmd.Value()) == IoctlCommand::IocRemapCommand) @@ -125,6 +127,37 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vector& ou params.offset = gpu.memory_manager->MapBufferEx(object->addr, object->size); } + // Create a new mapping entry for this operation. + ASSERT_MSG(buffer_mappings.find(params.offset) == buffer_mappings.end(), + "Offset is already mapped"); + + BufferMapping mapping{}; + mapping.nvmap_handle = params.nvmap_handle; + mapping.offset = params.offset; + mapping.size = object->size; + + buffer_mappings[params.offset] = mapping; + + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_as_gpu::UnmapBuffer(const std::vector& input, std::vector& output) { + IoctlUnmapBuffer params{}; + std::memcpy(¶ms, input.data(), input.size()); + + NGLOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); + + auto& gpu = Core::System::GetInstance().GPU(); + + auto itr = buffer_mappings.find(params.offset); + + ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping"); + + params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size); + + buffer_mappings.erase(itr->second.offset); + std::memcpy(output.data(), ¶ms, output.size()); return 0; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index f2dd0c3b33..d4c4b4db3e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include "common/common_types.h" @@ -30,6 +31,7 @@ private: IocMapBufferExCommand = 0xC0284106, IocBindChannelCommand = 0x40044101, IocGetVaRegionsCommand = 0xC0404108, + IocUnmapBufferCommand = 0xC0084105, }; struct IoctlInitalizeEx { @@ -76,6 +78,11 @@ private: }; static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size"); + struct IoctlUnmapBuffer { + u64_le offset; + }; + static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size"); + struct IoctlBindChannel { u32_le fd; }; @@ -98,12 +105,22 @@ private: static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, "IoctlGetVaRegions is incorrect size"); + struct BufferMapping { + u64 offset; + u64 size; + u32 nvmap_handle; + }; + + /// Map containing the nvmap object mappings in GPU memory. + std::unordered_map buffer_mappings; + u32 channel{}; u32 InitalizeEx(const std::vector& input, std::vector& output); u32 AllocateSpace(const std::vector& input, std::vector& output); u32 Remap(const std::vector& input, std::vector& output); u32 MapBufferEx(const std::vector& input, std::vector& output); + u32 UnmapBuffer(const std::vector& input, std::vector& output); u32 BindChannel(const std::vector& input, std::vector& output); u32 GetVARegions(const std::vector& input, std::vector& output); diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 25984439d6..5cefce9fce 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -58,6 +58,25 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) return gpu_addr; } +GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { + ASSERT((gpu_addr & PAGE_MASK) == 0); + + for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { + ASSERT(PageSlot(gpu_addr + offset) != static_cast(PageStatus::Allocated) && + PageSlot(gpu_addr + offset) != static_cast(PageStatus::Unmapped)); + PageSlot(gpu_addr + offset) = static_cast(PageStatus::Unmapped); + } + + // Delete the region mappings that are contained within the unmapped region + mapped_regions.erase(std::remove_if(mapped_regions.begin(), mapped_regions.end(), + [&](const MappedRegion& region) { + return region.gpu_addr <= gpu_addr && + region.gpu_addr + region.size < gpu_addr + size; + }), + mapped_regions.end()); + return gpu_addr; +} + boost::optional MemoryManager::FindFreeBlock(u64 size, u64 align) { GPUVAddr gpu_addr = 0; u64 free_space = 0; diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 08140c83a3..86765e72ab 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -25,6 +25,7 @@ public: GPUVAddr AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align); GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size); GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size); + GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size); boost::optional GpuToCpuAddress(GPUVAddr gpu_addr); std::vector CpuToGpuAddress(VAddr cpu_addr) const; From 525492428d3b1ccbe2096944a7525b242d4c9e7b Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 20 May 2018 14:23:49 -0500 Subject: [PATCH 2/2] GPU: Implemented the nvmap Free ioctl. It releases a reference to an nvmap object --- src/core/hle/service/nvdrv/devices/nvmap.cpp | 35 ++++++++++++++++++++ src/core/hle/service/nvdrv/devices/nvmap.h | 14 +++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 8d883209f6..d66fb3a9c5 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -30,6 +30,8 @@ u32 nvmap::ioctl(Ioctl command, const std::vector& input, std::vector& o return IocFromId(input, output); case IoctlCommand::Param: return IocParam(input, output); + case IoctlCommand::Free: + return IocFree(input, output); } UNIMPLEMENTED_MSG("Unimplemented ioctl"); @@ -45,6 +47,7 @@ u32 nvmap::IocCreate(const std::vector& input, std::vector& output) { object->id = next_id++; object->size = params.size; object->status = Object::Status::Created; + object->refcount = 1; u32 handle = next_handle++; handles[handle] = std::move(object); @@ -101,6 +104,8 @@ u32 nvmap::IocFromId(const std::vector& input, std::vector& output) { [&](const auto& entry) { return entry.second->id == params.id; }); ASSERT(itr != handles.end()); + itr->second->refcount++; + // Return the existing handle instead of creating a new one. params.handle = itr->first; @@ -142,4 +147,34 @@ u32 nvmap::IocParam(const std::vector& input, std::vector& output) { return 0; } +u32 nvmap::IocFree(const std::vector& input, std::vector& output) { + enum FreeFlags { + Freed = 0, + NotFreedYet = 1, + }; + + IocFreeParams params; + std::memcpy(¶ms, input.data(), sizeof(params)); + + NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); + + auto itr = handles.find(params.handle); + ASSERT(itr != handles.end()); + + itr->second->refcount--; + + params.refcount = itr->second->refcount; + params.size = itr->second->size; + + if (itr->second->refcount == 0) + params.flags = Freed; + else + params.flags = NotFreedYet; + + handles.erase(params.handle); + + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 431eb37736..5a30441672 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -34,6 +34,7 @@ public: u8 kind; VAddr addr; Status status; + u32 refcount; }; std::shared_ptr GetObject(u32 handle) const { @@ -59,7 +60,8 @@ private: FromId = 0xC0080103, Alloc = 0xC0200104, Param = 0xC00C0109, - GetId = 0xC008010E + GetId = 0xC008010E, + Free = 0xC0180105, }; struct IocCreateParams { @@ -102,11 +104,21 @@ private: u32_le value; }; + struct IocFreeParams { + u32_le handle; + INSERT_PADDING_BYTES(4); + u64_le refcount; + u32_le size; + u32_le flags; + }; + static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size"); + u32 IocCreate(const std::vector& input, std::vector& output); u32 IocAlloc(const std::vector& input, std::vector& output); u32 IocGetId(const std::vector& input, std::vector& output); u32 IocFromId(const std::vector& input, std::vector& output); u32 IocParam(const std::vector& input, std::vector& output); + u32 IocFree(const std::vector& input, std::vector& output); }; } // namespace Service::Nvidia::Devices