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:
commit
698254f048
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue