diff --git a/core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp b/core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp index b10c4571b..e1ca1d457 100644 --- a/core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp +++ b/core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp @@ -901,3 +901,28 @@ bool QueryDirectMapping(std::uintptr_t address, std::uintptr_t* start, std::uint *memoryType = it->second.memoryType; return true; } + +std::vector DirectMemoryViews(int64_t start, size_t len) { + const auto first = static_cast(start); + const auto last = first + len; + std::vector views; + std::lock_guard lock(g_directLock); + for (const auto& [base, mapping] : g_directMappings) { + const auto mappingLast = mapping.phys + (mapping.end - base); + if (mapping.phys >= last || mappingLast <= first) continue; + const auto viewStart = base + (std::max(first, mapping.phys) - mapping.phys); + const auto viewEnd = base + (std::min(last, mappingLast) - mapping.phys); + if (!views.empty() && views.back().address + views.back().bytes == viewStart) views.back().bytes += viewEnd - viewStart; + else views.push_back({viewStart, viewEnd - viewStart}); + } + return views; +} + +void UnmapDirectMemoryViews(const std::vector& views) { + if (views.empty()) return; + GuestAllocations::Mutation mutation; + for (const auto& view : views) { + Trace("unmap released view %p+0x%zx", reinterpret_cast(view.address), view.bytes); + UnmapRegistered(mutation, reinterpret_cast(view.address), view.bytes); + } +} diff --git a/core/libs/prx/libkernel/DirectMemory/DirectMemory.hpp b/core/libs/prx/libkernel/DirectMemory/DirectMemory.hpp index e2310818d..248234829 100644 --- a/core/libs/prx/libkernel/DirectMemory/DirectMemory.hpp +++ b/core/libs/prx/libkernel/DirectMemory/DirectMemory.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "prx/libkernel/KernelErrors.hpp" @@ -15,6 +16,14 @@ bool DirectMemoryCheckedFree(int64_t start, size_t len); void CreateDirectMemoryBacking(int64_t start, size_t len, int memoryType); bool QueryDirectMapping(std::uintptr_t address, std::uintptr_t* start, std::uintptr_t* end, std::uint64_t* offset, int* memoryType); void ForgetDirectMemory(int64_t start, size_t len); + +struct DirectMemoryView { + std::uintptr_t address; + std::size_t bytes; +}; + +std::vector DirectMemoryViews(int64_t start, size_t len); +void UnmapDirectMemoryViews(const std::vector& views); bool DirectMemoryFind(int64_t offset, bool findNext, int64_t* start, int64_t* end, int* memoryType); void DirectMemoryRetype(int64_t start, size_t len, int memoryType); size_t DirectMemoryFreeRun(uint64_t offset, uint64_t limit); diff --git a/core/libs/prx/libkernel/DirectMemory/Export.cpp b/core/libs/prx/libkernel/DirectMemory/Export.cpp index 5815eb4b7..73cbfb29f 100644 --- a/core/libs/prx/libkernel/DirectMemory/Export.cpp +++ b/core/libs/prx/libkernel/DirectMemory/Export.cpp @@ -217,7 +217,9 @@ int APS5_VABI sceKernelReleaseFlexibleMemory(void* addr, size_t len) { int APS5_VABI sceKernelReleaseDirectMemory(int64_t start, size_t len) { if (start < 0 || len == 0) return SCE_KERNEL_ERROR_EINVAL; + const auto views = DirectMemoryViews(start, len); DirectMemoryFree(start, len); + UnmapDirectMemoryViews(views); return 0; } @@ -312,7 +314,10 @@ int APS5_VABI sceKernelVirtualQuery(const void* addr, int flags, VirtualQueryInf int APS5_VABI sceKernelCheckedReleaseDirectMemory(int64_t start, size_t len) { if (start < 0 || (static_cast(start) & (PS5_PAGE_SIZE - 1)) != 0 || (len & (PS5_PAGE_SIZE - 1)) != 0) return SCE_KERNEL_ERROR_EINVAL; if (len == 0) return 0; - return DirectMemoryCheckedFree(start, len) ? 0 : SCE_KERNEL_ERROR_ENOENT; + const auto views = DirectMemoryViews(start, len); + if (!DirectMemoryCheckedFree(start, len)) return SCE_KERNEL_ERROR_ENOENT; + UnmapDirectMemoryViews(views); + return 0; } int APS5_VABI sceKernelMtypeprotect(const void* addr, size_t len, int type, int prot) { diff --git a/core/libs/tests/GuestMemory.cpp b/core/libs/tests/GuestMemory.cpp index 504b0ca38..1bfba3d05 100644 --- a/core/libs/tests/GuestMemory.cpp +++ b/core/libs/tests/GuestMemory.cpp @@ -279,13 +279,17 @@ static void CheckReleaseDirectMemoryClearsMappings() { Require(sceKernelVirtualQuery(mapped, 0, &split, sizeof(split)) == 0); Require(split.is_direct && split.offset == static_cast(phys)); VirtualQueryInfo dropped{}; - Require(sceKernelVirtualQuery(static_cast(mapped) + page, 0, &dropped, sizeof(dropped)) == 0); - Require(!dropped.is_direct && dropped.offset == 0); + const int droppedResult = sceKernelVirtualQuery(static_cast(mapped) + page, 0, &dropped, sizeof(dropped)); + Require(droppedResult != 0 || !dropped.is_direct); + void* alias = nullptr; + Require(sceKernelMapDirectMemory(&alias, page, 3, 0, phys, 0) == 0); Require(sceKernelReleaseDirectMemory(phys, page) == 0); - VirtualQueryInfo cleared{}; - Require(sceKernelVirtualQuery(mapped, 0, &cleared, sizeof(cleared)) == 0); - Require(!cleared.is_direct && cleared.offset == 0); - Require(sceKernelMunmap(mapped, page * 2) == 0); + for (void* view : {mapped, alias}) { + VirtualQueryInfo cleared{}; + Require(sceKernelVirtualQuery(view, 0, &cleared, sizeof(cleared)) != 0); + VirtualQueryInfo next{}; + if (sceKernelVirtualQuery(view, 1, &next, sizeof(next)) == 0) Require(next.start != reinterpret_cast(view)); + } } static void CheckFixedMappingReplacesPartialOverlap() {