From 9599e578b7a21466cccdd62baecd325acf3fb706 Mon Sep 17 00:00:00 2001 From: Stephen Miller Date: Sat, 3 May 2025 12:07:38 -0500 Subject: [PATCH] Fix mappings to high addresses The PS4's GPU can only handle 40bit addresses. Our texture cache and buffer cache were designed around these limits, and mapping to higher addresses would cause segmentation faults and access violations. To fix these crashes, only map to the GPU if the mapping is fully contained within the address space the GPU should access. I'm open to suggestions on how to make this cleaner --- src/core/memory.cpp | 27 +++++++++++++++++++-------- src/core/memory.h | 6 ++++++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 7610efd7d..d76cbb67e 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -310,7 +310,10 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) new_vma.is_exec = false; new_vma.phys_base = 0; - rasterizer->MapMemory(mapped_addr, size); + if (IsValidGpuMapping(mapped_addr, size)) { + rasterizer->MapMemory(mapped_addr, size); + } + return ORBIS_OK; } @@ -365,7 +368,10 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M if (type == VMAType::Flexible) { flexible_usage += size; } - rasterizer->MapMemory(mapped_addr, size); + + if (IsValidGpuMapping(mapped_addr, size)) { + rasterizer->MapMemory(mapped_addr, size); + } return ORBIS_OK; } @@ -420,7 +426,9 @@ void MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) { const auto start_in_vma = virtual_addr - vma_base_addr; const auto type = vma_base.type; - rasterizer->UnmapMemory(virtual_addr, size); + if (IsValidGpuMapping(virtual_addr, size)) { + rasterizer->UnmapMemory(virtual_addr, size); + } // Mark region as free and attempt to coalesce it with neighbours. const auto new_it = CarveVMA(virtual_addr, size); @@ -460,7 +468,10 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma if (type == VMAType::Flexible) { flexible_usage -= adjusted_size; } - rasterizer->UnmapMemory(virtual_addr, adjusted_size); + + if (IsValidGpuMapping(virtual_addr, size)) { + rasterizer->UnmapMemory(virtual_addr, size); + } // Mark region as free and attempt to coalesce it with neighbours. const auto new_it = CarveVMA(virtual_addr, adjusted_size); @@ -718,9 +729,9 @@ VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) } // If the requested address is beyond the maximum our code can handle, return an error. - auto max_search_address = 0x10000000000; + auto max_search_address = impl.UserVirtualBase() + impl.UserVirtualSize(); if (virtual_addr >= max_search_address) { - LOG_ERROR(Kernel_Vmm, "Address {:#x} is too high", virtual_addr); + LOG_ERROR(Kernel_Vmm, "Input address {:#x} is too high", virtual_addr); return -1; } @@ -749,8 +760,8 @@ VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) // Make sure the address is within our defined bounds if (virtual_addr >= max_search_address) { - LOG_ERROR(Kernel_Vmm, "Address {:#x} is too high", virtual_addr); - return -1; + // There are no free mappings within our safely usable address space. + break; } // If there's enough space in the VMA, return the address. diff --git a/src/core/memory.h b/src/core/memory.h index 4ccd7cc5d..3a204eb96 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -157,6 +157,12 @@ public: return impl.SystemReservedVirtualBase(); } + bool IsValidGpuMapping(VAddr virtual_addr, u64 size) { + // The PS4's GPU can only handle 40 bit addresses. + const VAddr max_gpu_address{0x10000000000}; + return virtual_addr + size < max_gpu_address; + } + bool IsValidAddress(const void* addr) const noexcept { const VAddr virtual_addr = reinterpret_cast(addr); const auto end_it = std::prev(vma_map.end());