diff --git a/core/libs/prx/libkernel/DirectMemory/Export.cpp b/core/libs/prx/libkernel/DirectMemory/Export.cpp index da96d2008..a08440be7 100644 --- a/core/libs/prx/libkernel/DirectMemory/Export.cpp +++ b/core/libs/prx/libkernel/DirectMemory/Export.cpp @@ -207,6 +207,10 @@ int APS5_VABI sceKernelMunmap(uint64_t vaddr, size_t len) { return result; } +int APS5_VABI sceKernelReleaseFlexibleMemory(void* addr, size_t len) { + return sceKernelMunmap(reinterpret_cast(addr), len); +} + int APS5_VABI sceKernelReleaseDirectMemory(int64_t start, size_t len) { if (start < 0 || len == 0) return SCE_KERNEL_ERROR_EINVAL; DirectMemoryFree(start, len); diff --git a/core/libs/prx/libkernel/Unimplemented.cpp b/core/libs/prx/libkernel/Unimplemented.cpp index 96466573a..18df01488 100644 --- a/core/libs/prx/libkernel/Unimplemented.cpp +++ b/core/libs/prx/libkernel/Unimplemented.cpp @@ -15,11 +15,6 @@ int APS5_VABI __tls_get_addr_nid_postfix(void) { return 0; } -int APS5_VABI sceKernelReleaseFlexibleMemory(void) { - NotImplemented_nid_no_patch("teiItL2boFw"); - return 0; -} - // Canonical lib is libScePosix (dead import of Cyberpunk 2077): 35 of its // 36 sibling imports resolve to libkernel, and libScePosix is not in the // game's NEEDED list so only a NEEDED module can satisfy the loader here. diff --git a/core/libs/tests/GuestMemory.cpp b/core/libs/tests/GuestMemory.cpp index 27dd441ab..59b3fb0ac 100644 --- a/core/libs/tests/GuestMemory.cpp +++ b/core/libs/tests/GuestMemory.cpp @@ -41,6 +41,7 @@ int APS5_VABI sceKernelMapNamedFlexibleMemoryInternal(void**, std::size_t, int, int APS5_VABI sceKernelAvailableFlexibleMemorySize(std::size_t*); int APS5_VABI sceKernelMapFlexibleMemory(void**, std::size_t, int, int); int APS5_VABI sceKernelMunmap(void*, std::size_t); +int APS5_VABI sceKernelReleaseFlexibleMemory(void*, std::size_t); int APS5_VABI sceKernelMprotect(const void*, std::size_t, int); int APS5_VABI sceKernelVirtualQuery(const void*, int, VirtualQueryInfo*, std::uint64_t); int APS5_VABI sceKernelSetVirtualRangeName(const void*, std::uint64_t, const char*); @@ -77,6 +78,23 @@ static const char* NameAt(const void* address) { return info.name; } +static void CheckReleaseFlexibleMemory() { + constexpr std::size_t length = 0x10000; + std::size_t before = 0; + std::size_t available = 0; + Require(sceKernelAvailableFlexibleMemorySize(&before) == 0); + void* mapped = nullptr; + Require(sceKernelMapFlexibleMemory(&mapped, length, 3, 0) == 0 && mapped != nullptr); + static_cast(mapped)[length - 1] = 1; + Require(sceKernelAvailableFlexibleMemorySize(&available) == 0 && available == before - length); + Require(sceKernelReleaseFlexibleMemory(mapped, length) == 0); + Require(sceKernelAvailableFlexibleMemorySize(&available) == 0 && available == before); + void* again = mapped; + Require(sceKernelMapFlexibleMemory(&again, length, 3, 0x90) == 0 && again == mapped); + Require(static_cast(again)[length - 1] == 0); + Require(sceKernelMunmap(again, length) == 0); +} + static void CheckNamedAndHintedMappings() { constexpr std::size_t length = 0x10000; void* first = nullptr; @@ -830,6 +848,7 @@ static void CheckDirectMemoryWriteWatch() { #endif int main() { + CheckReleaseFlexibleMemory(); CheckNamedAndHintedMappings(); CheckInternalNamedFlexibleMapping(); CheckBatchMapStopsAtInvalidEntry(); diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 8543e95d7..885bcbb8e 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -67,6 +67,7 @@ Throughout the project, every function at every stage either **does exactly what - [sceAgcDcbPrimeUtcl2](../../core/libs/prx/libSceAgc/DcbState/src/Display.cpp) (libSceAgc) - assumed to write the packet `sceAgcAcbPrimeUtcl2` writes: PRIME_UTCL2 has the same 5 dwords on the graphics and compute rings. Not confirmed on a title. Both write a NOP of that size, as there is no TLB to prime - [sceKernelInternalMemoryGetModuleSegmentInfo](../../core/libs/prx/libkernel/Module/src/Module.cpp) (libkernel) - unknown signature - [sceKernelGetModuleInfoFromAddr](../../core/libs/prx/libkernel/Module/src/ModuleInfo.cpp) (libkernel) - the 0x1A8-byte info layout follows the [shadPS4](https://github.com/shadps4-emu/shadPS4) reimplementation; a title's `libc.prx` reads only `id` (PPSA14632). The meaning of `flags` is unknown; only 2 is accepted, as is only the full `st_size`. The id is the `sceKernelLoadStartModule` handle of an image opened through the loader and otherwise a stable id from the same range. The name is the host file name without `.guest.prx`. An image with more than 4 load segments (the converted executable has 7) reports its first 4. `tls_offset` is 0 and `ref_count` is 1, as the host loader exposes neither. Not implemented on Windows +- [sceKernelReleaseFlexibleMemory](../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) - unmaps the range like sceKernelMunmap, as fpPS4 and prosper do; whether the console keeps the virtual range reserved after releasing its pages is unverified - [sceKernelSyncOnAddressWait](../../core/libs/prx/libkernel/SyncOnAddress/src/SyncOnAddress.cpp) (libkernel) - the only known caller passes a null timeout and a name string as the fourth argument; the timeout is assumed to point to microseconds like the other kernel waits, and the name is ignored - [sceKernelSyncOnAddressWait8 / sceKernelSyncOnAddressWait16 / sceKernelSyncOnAddressWait32 / sceKernelSyncOnAddressWait64](../../core/libs/prx/libkernel/SyncOnAddress/src/SyncOnAddress.cpp) (libkernel) - libc's `__std_atomic_wait_direct_8/16/32/64` pass a microsecond timeout or null and ignore the result; the by-value comparand, return values and the 2- and 8-byte alignment of Wait16 and Wait64 are assumed - [sceKernelAioSubmitReadCommandsMultiple / sceKernelAioSubmitWriteCommandsMultiple / sceKernelAioWaitRequests / sceKernelAioCancelRequest / sceKernelAioCancelRequests / sceKernelAioDeleteRequests](../../core/libs/prx/libkernel/Aio/src/Aio.cpp) (libkernel) - behaviour from shadPS4 (one id per request for the Multiple submits, cancel turns any request aborted and id 0 reports processing, wait mode 2 returns once one request completed); the 128 requests or ids per batch limit is from SharpProspero, with an unknown error, so larger batches throw; wait modes other than 1 (and) and 2 (or) throw; null pointers, negative counts and invalid ids return the codes the single-request siblings use, checked before any state is written