Merge pull request #2236 from mrk1987/fix/release-direct-memory-unmaps-views

fix(libkernel): unmap direct memory views when the memory is released
This commit is contained in:
Dean 2026-10-10 17:45:32 +13:00 committed by GitHub
commit 698254f048
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 50 additions and 7 deletions

View File

@ -901,3 +901,28 @@ bool QueryDirectMapping(std::uintptr_t address, std::uintptr_t* start, std::uint
*memoryType = it->second.memoryType;
return true;
}
std::vector<DirectMemoryView> DirectMemoryViews(int64_t start, size_t len) {
const auto first = static_cast<std::uint64_t>(start);
const auto last = first + len;
std::vector<DirectMemoryView> 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<DirectMemoryView>& views) {
if (views.empty()) return;
GuestAllocations::Mutation mutation;
for (const auto& view : views) {
Trace("unmap released view %p+0x%zx", reinterpret_cast<void*>(view.address), view.bytes);
UnmapRegistered(mutation, reinterpret_cast<void*>(view.address), view.bytes);
}
}

View File

@ -3,6 +3,7 @@
#include <cstdint>
#include <cstddef>
#include <vector>
#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<DirectMemoryView> DirectMemoryViews(int64_t start, size_t len);
void UnmapDirectMemoryViews(const std::vector<DirectMemoryView>& 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);

View File

@ -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<uint64_t>(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) {

View File

@ -279,13 +279,17 @@ static void CheckReleaseDirectMemoryClearsMappings() {
Require(sceKernelVirtualQuery(mapped, 0, &split, sizeof(split)) == 0);
Require(split.is_direct && split.offset == static_cast<std::uint64_t>(phys));
VirtualQueryInfo dropped{};
Require(sceKernelVirtualQuery(static_cast<unsigned char*>(mapped) + page, 0, &dropped, sizeof(dropped)) == 0);
Require(!dropped.is_direct && dropped.offset == 0);
const int droppedResult = sceKernelVirtualQuery(static_cast<unsigned char*>(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<std::uintptr_t>(view));
}
}
static void CheckFixedMappingReplacesPartialOverlap() {