From 241563d15c8b831a2d2b7c360d570cc721903d14 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 3 Mar 2019 23:17:35 -0500 Subject: gpu: Move GPUVAddr definition to common_types. --- src/video_core/memory_manager.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/video_core/memory_manager.h') diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 425e2f31c..bb87fa24d 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -13,9 +13,6 @@ namespace Tegra { -/// Virtual addresses in the GPU's memory map are 64 bit. -using GPUVAddr = u64; - class MemoryManager final { public: MemoryManager(); -- cgit v1.2.3 From 22d3dfbcd4c606d40e5ae36970db4661c302859f Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 3 Mar 2019 23:54:16 -0500 Subject: gpu: Rewrite virtual memory manager using PageTable. --- src/video_core/memory_manager.h | 162 ++++++++++++++++++++++++++++------------ 1 file changed, 115 insertions(+), 47 deletions(-) (limited to 'src/video_core/memory_manager.h') diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index bb87fa24d..ac1b42936 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -1,79 +1,147 @@ -// Copyright 2018 yuzu emulator team +// Copyright 2018 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once -#include -#include +#include #include -#include #include "common/common_types.h" +#include "common/page_table.h" namespace Tegra { +/** + * Represents a VMA in an address space. A VMA is a contiguous region of virtual addressing space + * with homogeneous attributes across its extents. In this particular implementation each VMA is + * also backed by a single host memory allocation. + */ +struct VirtualMemoryArea { + enum class Type : u8 { + Unmapped, + Allocated, + Mapped, + }; + + /// Virtual base address of the region. + GPUVAddr base{}; + /// Size of the region. + u64 size{}; + /// Memory area mapping type. + Type type{Type::Unmapped}; + /// CPU memory mapped address corresponding to this memory area. + VAddr backing_addr{}; + /// Offset into the backing_memory the mapping starts from. + std::size_t offset{}; + /// Pointer backing this VMA. + u8* backing_memory{}; + + /// Tests if this area can be merged to the right with `next`. + bool CanBeMergedWith(const VirtualMemoryArea& next) const; +}; + class MemoryManager final { public: MemoryManager(); GPUVAddr AllocateSpace(u64 size, u64 align); 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 MapBufferEx(GPUVAddr cpu_addr, u64 size); + GPUVAddr MapBufferEx(GPUVAddr cpu_addr, GPUVAddr gpu_addr, u64 size); GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size); - GPUVAddr GetRegionEnd(GPUVAddr region_start) const; std::optional GpuToCpuAddress(GPUVAddr gpu_addr); - static constexpr u64 PAGE_BITS = 16; - static constexpr u64 PAGE_SIZE = 1 << PAGE_BITS; - static constexpr u64 PAGE_MASK = PAGE_SIZE - 1; - - u8 Read8(GPUVAddr addr); - u16 Read16(GPUVAddr addr); - u32 Read32(GPUVAddr addr); - u64 Read64(GPUVAddr addr); + template + T Read(GPUVAddr vaddr); - void Write8(GPUVAddr addr, u8 data); - void Write16(GPUVAddr addr, u16 data); - void Write32(GPUVAddr addr, u32 data); - void Write64(GPUVAddr addr, u64 data); + template + void Write(GPUVAddr vaddr, T data); u8* GetPointer(GPUVAddr vaddr); void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size); void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); - void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size); + void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); private: - enum class PageStatus : u64 { - Unmapped = 0xFFFFFFFFFFFFFFFFULL, - Allocated = 0xFFFFFFFFFFFFFFFEULL, - Reserved = 0xFFFFFFFFFFFFFFFDULL, - }; - - std::optional FindFreeBlock(GPUVAddr region_start, u64 size, u64 align, - PageStatus status); - VAddr& PageSlot(GPUVAddr gpu_addr); - - static constexpr u64 MAX_ADDRESS{0x10000000000ULL}; - static constexpr u64 PAGE_TABLE_BITS{10}; - static constexpr u64 PAGE_TABLE_SIZE{1 << PAGE_TABLE_BITS}; - static constexpr u64 PAGE_TABLE_MASK{PAGE_TABLE_SIZE - 1}; - static constexpr u64 PAGE_BLOCK_BITS{14}; - static constexpr u64 PAGE_BLOCK_SIZE{1 << PAGE_BLOCK_BITS}; - static constexpr u64 PAGE_BLOCK_MASK{PAGE_BLOCK_SIZE - 1}; - - using PageBlock = std::array; - std::array, PAGE_TABLE_SIZE> page_table{}; - - struct MappedRegion { - VAddr cpu_addr; - GPUVAddr gpu_addr; - u64 size; - }; + using VMAMap = std::map; + using VMAHandle = VMAMap::const_iterator; + using VMAIter = VMAMap::iterator; + + void MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type, + VAddr backing_addr = 0); + void MapMemoryRegion(GPUVAddr base, u64 size, u8* target, VAddr backing_addr); + void UnmapRegion(GPUVAddr base, u64 size); + + /// Finds the VMA in which the given address is included in, or `vma_map.end()`. + VMAHandle FindVMA(GPUVAddr target) const; + + VMAHandle AllocateMemory(GPUVAddr target, std::size_t offset, u64 size); + + /** + * Maps an unmanaged host memory pointer at a given address. + * + * @param target The guest address to start the mapping at. + * @param memory The memory to be mapped. + * @param size Size of the mapping. + * @param state MemoryState tag to attach to the VMA. + */ + VMAHandle MapBackingMemory(GPUVAddr target, u8* memory, u64 size, VAddr backing_addr); + + /// Unmaps a range of addresses, splitting VMAs as necessary. + void UnmapRange(GPUVAddr target, u64 size); + + /// Converts a VMAHandle to a mutable VMAIter. + VMAIter StripIterConstness(const VMAHandle& iter); + + /// Unmaps the given VMA. + VMAIter Unmap(VMAIter vma); + + /** + * Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing + * the appropriate error checking. + */ + VMAIter CarveVMA(GPUVAddr base, u64 size); + + /** + * Splits the edges of the given range of non-Free VMAs so that there is a VMA split at each + * end of the range. + */ + VMAIter CarveVMARange(GPUVAddr base, u64 size); + + /** + * Splits a VMA in two, at the specified offset. + * @returns the right side of the split, with the original iterator becoming the left side. + */ + VMAIter SplitVMA(VMAIter vma, u64 offset_in_vma); + + /** + * Checks for and merges the specified VMA with adjacent ones if possible. + * @returns the merged VMA or the original if no merging was possible. + */ + VMAIter MergeAdjacent(VMAIter vma); + + /// Updates the pages corresponding to this VMA so they match the VMA's attributes. + void UpdatePageTableForVMA(const VirtualMemoryArea& vma); + + GPUVAddr FindFreeRegion(GPUVAddr region_start, u64 size, u64 align, + VirtualMemoryArea::Type vma_type); - std::vector mapped_regions; +private: + static constexpr u64 page_bits{16}; + static constexpr u64 page_size{1 << page_bits}; + static constexpr u64 page_mask{page_size - 1}; + + /// Address space in bits, this is fairly arbitrary but sufficiently large. + static constexpr u32 address_space_width = 39; + /// Start address for mapping, this is fairly arbitrary but must be non-zero. + static constexpr GPUVAddr address_space_base = 0x100000; + /// End of address space, based on address space in bits. + static constexpr GPUVAddr address_space_end = 1ULL << address_space_width; + + Common::PageTable page_table{page_bits}; + VMAMap vma_map; }; } // namespace Tegra -- cgit v1.2.3 From 197dcf0b5e426993f760374353cafb07126d45b2 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 9 Mar 2019 14:06:51 -0500 Subject: memory_manager: Add protections for invalid GPU addresses. - Avoid a crash in Xenoblade Chronicles 2. --- src/video_core/memory_manager.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src/video_core/memory_manager.h') diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index ac1b42936..76fa3d916 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -46,19 +46,19 @@ public: MemoryManager(); GPUVAddr AllocateSpace(u64 size, u64 align); - GPUVAddr AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align); + GPUVAddr AllocateSpace(GPUVAddr addr, u64 size, u64 align); GPUVAddr MapBufferEx(GPUVAddr cpu_addr, u64 size); - GPUVAddr MapBufferEx(GPUVAddr cpu_addr, GPUVAddr gpu_addr, u64 size); - GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size); - std::optional GpuToCpuAddress(GPUVAddr gpu_addr); + GPUVAddr MapBufferEx(GPUVAddr cpu_addr, GPUVAddr addr, u64 size); + GPUVAddr UnmapBuffer(GPUVAddr addr, u64 size); + std::optional GpuToCpuAddress(GPUVAddr addr); template - T Read(GPUVAddr vaddr); + T Read(GPUVAddr addr); template - void Write(GPUVAddr vaddr, T data); + void Write(GPUVAddr addr, T data); - u8* GetPointer(GPUVAddr vaddr); + u8* GetPointer(GPUVAddr addr); void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size); void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); @@ -69,6 +69,7 @@ private: using VMAHandle = VMAMap::const_iterator; using VMAIter = VMAMap::iterator; + bool IsAddressValid(GPUVAddr addr) const; void MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type, VAddr backing_addr = 0); void MapMemoryRegion(GPUVAddr base, u64 size, u8* target, VAddr backing_addr); -- cgit v1.2.3 From 72837e4b3d312ac6d7e5114c7b6e370006d46921 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 20 Mar 2019 22:28:35 -0400 Subject: memory_manager: Bug fixes and further cleanup. --- src/video_core/memory_manager.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/video_core/memory_manager.h') diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 76fa3d916..60ba6b858 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -47,8 +47,8 @@ public: GPUVAddr AllocateSpace(u64 size, u64 align); GPUVAddr AllocateSpace(GPUVAddr addr, u64 size, u64 align); - GPUVAddr MapBufferEx(GPUVAddr cpu_addr, u64 size); - GPUVAddr MapBufferEx(GPUVAddr cpu_addr, GPUVAddr addr, u64 size); + GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size); + GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr addr, u64 size); GPUVAddr UnmapBuffer(GPUVAddr addr, u64 size); std::optional GpuToCpuAddress(GPUVAddr addr); @@ -96,8 +96,8 @@ private: /// Converts a VMAHandle to a mutable VMAIter. VMAIter StripIterConstness(const VMAHandle& iter); - /// Unmaps the given VMA. - VMAIter Unmap(VMAIter vma); + /// Marks as the specfied VMA as allocated. + VMAIter Allocate(VMAIter vma); /** * Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing @@ -135,11 +135,11 @@ private: static constexpr u64 page_mask{page_size - 1}; /// Address space in bits, this is fairly arbitrary but sufficiently large. - static constexpr u32 address_space_width = 39; + static constexpr u32 address_space_width{39}; /// Start address for mapping, this is fairly arbitrary but must be non-zero. - static constexpr GPUVAddr address_space_base = 0x100000; + static constexpr GPUVAddr address_space_base{0x100000}; /// End of address space, based on address space in bits. - static constexpr GPUVAddr address_space_end = 1ULL << address_space_width; + static constexpr GPUVAddr address_space_end{1ULL << address_space_width}; Common::PageTable page_table{page_bits}; VMAMap vma_map; -- cgit v1.2.3 From 2117edd0f848cd7bc35bdbb1495ca10649715625 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 20 Mar 2019 23:12:28 -0400 Subject: memory_manager: Cleanup FindFreeRegion. --- src/video_core/memory_manager.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/video_core/memory_manager.h') diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 60ba6b858..34744bb27 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -126,8 +126,8 @@ private: /// Updates the pages corresponding to this VMA so they match the VMA's attributes. void UpdatePageTableForVMA(const VirtualMemoryArea& vma); - GPUVAddr FindFreeRegion(GPUVAddr region_start, u64 size, u64 align, - VirtualMemoryArea::Type vma_type); + /// Finds a free (unmapped region) of the specified size starting at the specified address. + GPUVAddr FindFreeRegion(GPUVAddr region_start, u64 size); private: static constexpr u64 page_bits{16}; -- cgit v1.2.3