diff --git a/core/libs/prx/libkernel/DirectMemory/DirectMemory.hpp b/core/libs/prx/libkernel/DirectMemory/DirectMemory.hpp index 988291463..591dabb17 100644 --- a/core/libs/prx/libkernel/DirectMemory/DirectMemory.hpp +++ b/core/libs/prx/libkernel/DirectMemory/DirectMemory.hpp @@ -26,6 +26,7 @@ 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); +bool DirectMemoryLargestFree(int64_t searchStart, int64_t searchEnd, size_t alignment, int64_t* startOut, size_t* sizeOut); size_t DirectMemoryFreeRun(uint64_t offset, uint64_t limit); int DoMapDirect(void** addr, size_t len, int prot, int flags, int64_t physStart, size_t alignment); int DoMapAnon(void** addr, size_t len, int prot, int flags, size_t alignment = PS5_PAGE_SIZE); diff --git a/core/libs/prx/libkernel/DirectMemory/Export.cpp b/core/libs/prx/libkernel/DirectMemory/Export.cpp index bee6dd534..91c0a1f6b 100644 --- a/core/libs/prx/libkernel/DirectMemory/Export.cpp +++ b/core/libs/prx/libkernel/DirectMemory/Export.cpp @@ -133,12 +133,11 @@ int APS5_VABI sceKernelAllocateMainDirectMemory(size_t len, size_t alignment, in int APS5_VABI sceKernelAvailableDirectMemorySize(int64_t search_start, int64_t search_end, size_t alignment, int64_t* phys_addr_out, size_t* size_out) { if (!phys_addr_out || !size_out) return SCE_KERNEL_ERROR_EINVAL; - int64_t tmpPhys = 0; - int ret = DirectMemoryAlloc(search_start, search_end, PS5_PAGE_SIZE, alignment, -1, &tmpPhys); - if (ret != 0) { *phys_addr_out = 0; *size_out = 0; return ret; } - DirectMemoryFree(tmpPhys, PS5_PAGE_SIZE); - *phys_addr_out = tmpPhys; - *size_out = DirectMemoryFreeRun(static_cast(tmpPhys), static_cast(search_end)); + int64_t start = 0; + size_t size = 0; + if (!DirectMemoryLargestFree(search_start, search_end, alignment, &start, &size)) { *phys_addr_out = 0; *size_out = 0; return SCE_KERNEL_ERROR_EAGAIN; } + *phys_addr_out = start; + *size_out = size; return 0; } diff --git a/core/libs/prx/libkernel/DirectMemory/MemoryPool.cpp b/core/libs/prx/libkernel/DirectMemory/MemoryPool.cpp index 645098b71..d7c6c65d7 100644 --- a/core/libs/prx/libkernel/DirectMemory/MemoryPool.cpp +++ b/core/libs/prx/libkernel/DirectMemory/MemoryPool.cpp @@ -72,6 +72,34 @@ struct PhysicalMemoryPool { } } + bool LargestFree(int64_t searchStart, int64_t searchEnd, size_t alignment, int64_t* startOut, size_t* sizeOut) { + std::lock_guard lock(_mutex); + if (searchStart < 0 || searchEnd <= searchStart) return false; + const uint64_t align = (alignment == 0) ? PS5_PAGE_SIZE : alignment; + const uint64_t limit = std::min(static_cast(searchEnd), DIRECT_MEMORY_SIZE); + uint64_t bestStart = 0; + uint64_t bestSize = 0; + uint64_t cur = (static_cast(searchStart) + PS5_PAGE_SIZE - 1) & ~static_cast(PS5_PAGE_SIZE - 1); + while (cur + PS5_PAGE_SIZE <= limit) { + if (!_isFree(cur, PS5_PAGE_SIZE)) { + cur += PS5_PAGE_SIZE; + continue; + } + uint64_t runEnd = cur; + while (runEnd + PS5_PAGE_SIZE <= limit && _isFree(runEnd, PS5_PAGE_SIZE)) runEnd += PS5_PAGE_SIZE; + const uint64_t alignedStart = (cur + align - 1) & ~(align - 1); + if (alignedStart < runEnd && runEnd - alignedStart > bestSize) { + bestStart = alignedStart; + bestSize = runEnd - alignedStart; + } + cur = runEnd; + } + if (bestSize == 0) return false; + *startOut = static_cast(bestStart); + *sizeOut = static_cast(bestSize); + return true; + } + size_t FreeRun(uint64_t offset, uint64_t limit) { std::lock_guard lock(_mutex); uint64_t cur = offset & ~static_cast(PS5_PAGE_SIZE - 1); @@ -145,6 +173,10 @@ void DirectMemoryRetype(int64_t start, size_t len, int memoryType) { PhysicalMemoryPool::Instance().Retype(static_cast(start), len, memoryType); } +bool DirectMemoryLargestFree(int64_t searchStart, int64_t searchEnd, size_t alignment, int64_t* startOut, size_t* sizeOut) { + return PhysicalMemoryPool::Instance().LargestFree(searchStart, searchEnd, alignment, startOut, sizeOut); +} + size_t DirectMemoryFreeRun(uint64_t offset, uint64_t limit) { return PhysicalMemoryPool::Instance().FreeRun(offset, limit); } diff --git a/core/libs/tests/GuestMemory.cpp b/core/libs/tests/GuestMemory.cpp index 1e7d99326..e32ffcbb4 100644 --- a/core/libs/tests/GuestMemory.cpp +++ b/core/libs/tests/GuestMemory.cpp @@ -59,6 +59,7 @@ int APS5_VABI sceKernelClearVirtualRangeName(const void*, std::uint64_t); int APS5_VABI sceKernelAllocateDirectMemory(std::int64_t, std::int64_t, std::size_t, std::size_t, int, std::int64_t*); int APS5_VABI sceKernelMapDirectMemory(void**, std::size_t, int, int, std::int64_t, std::size_t); int APS5_VABI sceKernelReleaseDirectMemory(std::int64_t, std::size_t); +int APS5_VABI sceKernelAvailableDirectMemorySize(std::int64_t, std::int64_t, std::size_t, std::int64_t*, std::size_t*); int APS5_VABI sceKernelCheckedReleaseDirectMemory(std::int64_t, std::size_t); int APS5_VABI sceKernelReserveVirtualRange(void**, std::size_t, int, std::size_t); int APS5_VABI sceKernelMemoryPoolReserve(void*, std::size_t, std::size_t, int, void**); @@ -1621,10 +1622,41 @@ static void CheckVirtualQueryPartialMunmap() { Require(munmap_nid_postfix(memory + page * 2, page) == 0); } + +static void CheckAvailableDirectMemoryIsLargestFreeBlock() { + constexpr std::int64_t window = 0x4000000; + const std::int64_t top = static_cast(DIRECT_MEMORY_SIZE) - window; + std::int64_t first = -1; + std::int64_t gap = -1; + std::int64_t last = -1; + Require(sceKernelAllocateDirectMemory(top, top + window, 0x200000, 0x200000, 0, &first) == 0 && first == top); + Require(sceKernelAllocateDirectMemory(top, top + window, 0x400000, 0x200000, 0, &gap) == 0 && gap == top + 0x200000); + Require(sceKernelAllocateDirectMemory(top, top + window, 0x200000, 0x200000, 0, &last) == 0 && last == top + 0x600000); + Require(sceKernelReleaseDirectMemory(gap, 0x400000) == 0); + + std::int64_t start = -1; + std::size_t size = 0; + Require(sceKernelAvailableDirectMemorySize(top, top + window, 0, &start, &size) == 0); + Require(start == top + 0x800000 && size == static_cast(window - 0x800000)); + Require(sceKernelAvailableDirectMemorySize(top, top + 0x800000, 0, &start, &size) == 0); + Require(start == top + 0x200000 && size == 0x400000); + Require(sceKernelAvailableDirectMemorySize(top, top + window, 0x1000000, &start, &size) == 0); + Require(start == top + 0x1000000 && size == static_cast(window - 0x1000000)); + Require(sceKernelAvailableDirectMemorySize(top, top + 0x200000, 0, &start, &size) == SCE_KERNEL_ERROR_EAGAIN); + Require(start == 0 && size == 0); + Require(sceKernelAvailableDirectMemorySize(top, top + window, 0, nullptr, &size) == SCE_KERNEL_ERROR_EINVAL); + + Require(sceKernelReleaseDirectMemory(first, 0x200000) == 0); + Require(sceKernelReleaseDirectMemory(last, 0x200000) == 0); + Require(sceKernelAvailableDirectMemorySize(top, top + window, 0, &start, &size) == 0); + Require(start == top && size == static_cast(window)); +} + int main() { #ifdef _WIN32 _putenv_s("APS5_PIN_WAIT_MS", "1000"); #endif + CheckAvailableDirectMemoryIsLargestFreeBlock(); CheckVirtualQuerySplitFlexibleRanges(); CheckVirtualQueryForNonReadableGuestRange(); CheckReleaseFlexibleMemory();