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
This commit is contained in:
Stephen Miller 2025-05-03 12:07:38 -05:00
parent 31ae988abe
commit 9599e578b7
2 changed files with 25 additions and 8 deletions

View File

@ -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.

View File

@ -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<VAddr>(addr);
const auto end_it = std::prev(vma_map.end());