feat(libkernel): implement sceKernelMapNamedSystemFlexibleMemory

This commit is contained in:
zaryabzip committed 2026-10-26 13:59:25 +05:00
1 parent fb5afb3ef7
commit b8ab1b1a5c
7 files changed
+112 -6

No files matched your search

+10
View File
@@ -1781,6 +1781,16 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
endforeach()
add_test_executable(guest_system_flexible_memory_tests ${LIBS_INCLUDE_DIR}/tests/GuestSystemFlexibleMemory.cpp)
target_include_directories(guest_system_flexible_memory_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_system_flexible_memory_tests PRIVATE libkernel libc)
configure_windows_unwind(guest_system_flexible_memory_tests)
add_test(NAME guest_system_flexible_memory COMMAND guest_system_flexible_memory_tests)
set_tests_properties(guest_system_flexible_memory PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_system_flexible_memory PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_page_table_stats_tests ${LIBS_INCLUDE_DIR}/tests/GuestPageTableStats.cpp)
target_include_directories(guest_page_table_stats_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_page_table_stats_tests PRIVATE libkernel libc)
@@ -567,6 +567,18 @@ int APS5_VABI sceKernelBatchMap2(KernelBatchMapEntry* entries, int num_entries,
return result;
}
int32_t APS5_VABI sceKernelMapNamedSystemFlexibleMemory(void** addr_in_out, size_t len, int prot, int flags, const char* name) {
constexpr size_t MaximumNameLength = 32;
if (len == 0 || (len & (PS5_PAGE_SIZE - 1)) != 0) return SCE_KERNEL_ERROR_EINVAL;
if (name == nullptr) throw std::runtime_error("sceKernelMapNamedSystemFlexibleMemory: null name");
if (strnlen(name, MaximumNameLength) >= MaximumNameLength) throw std::runtime_error("sceKernelMapNamedSystemFlexibleMemory: name of 32 bytes or more");
const int result = DoMapAnon(addr_in_out, len, prot, flags);
if (result != 0) return result;
_releaseFlexible(reinterpret_cast<uintptr_t>(*addr_in_out), len);
sceKernelSetVirtualRangeName(*addr_in_out, len, name);
return 0;
}
}
bool GuestRangeMapped(std::uintptr_t start, std::uintptr_t end) {
+1
View File
@@ -215,6 +215,7 @@ int APS5_VABI sceKernelMapFlexibleMemory(void** addrInOut, size_t len, int prot,
int APS5_VABI sceKernelMapNamedDirectMemory(void** addr, size_t len, int prot, int flags, int64_t directMemoryStart, size_t alignment, const char* name);
int32_t APS5_VABI sceKernelMapNamedFlexibleMemory(void** addrInOut, size_t len, int prot, int flags, const char* name);
int32_t APS5_VABI sceKernelMapNamedFlexibleMemoryInternal(void** addrInOut, size_t len, int prot, int flags, const char* name);
int32_t APS5_VABI sceKernelMapNamedSystemFlexibleMemory(void** addrInOut, size_t len, int prot, int flags, const char* name);
int APS5_VABI sceKernelMprotect(const void* addr, size_t len, int prot);
int APS5_VABI sceKernelMunmap(uint64_t vaddr, size_t len);
int APS5_VABI sceKernelMmap(void* addr, size_t len, int prot, int flags, int fd, int64_t offset, void** res);
@@ -1523,11 +1523,6 @@ int APS5_VABI sceKernelLwfsWrite(void) {
return 0;
}
int APS5_VABI sceKernelMapNamedSystemFlexibleMemory(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceKernelMapSanitizerShadowMemory(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -387,7 +387,6 @@ int APS5_VABI sceKernelLwfsLseek(void);
int APS5_VABI sceKernelLwfsSetAttribute(void);
int APS5_VABI sceKernelLwfsTrimBlock(void);
int APS5_VABI sceKernelLwfsWrite(void);
int APS5_VABI sceKernelMapNamedSystemFlexibleMemory(void);
int APS5_VABI sceKernelMapSanitizerShadowMemory(void);
int APS5_VABI sceKernelMapToolMemory(void);
int APS5_VABI sceKernelMapTraceMemory(void);
@@ -0,0 +1,88 @@
#include "prx/libkernel/include/Api.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include "prx/libkernel/KernelErrors.hpp"
#include "prx/libkernel/DirectMemory/DirectMemory.hpp"
#include "SceTypes.hpp"
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <source_location>
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif
static void Require(bool condition, std::source_location location = std::source_location::current()) {
if (!condition) {
std::fprintf(stderr, "System flexible memory check failed at %s:%u\n", location.file_name(), static_cast<unsigned>(location.line()));
std::abort();
}
}
using MapSystemFlexible = int32_t (APS5_VABI*)(void**, std::size_t, int, int, const char*);
static MapSystemFlexible mapSystemFlexible = nullptr;
static MapSystemFlexible Resolve() {
#ifdef _WIN32
const HMODULE module = GetModuleHandleA("libkernel.prx");
Require(module != nullptr);
auto* symbol = reinterpret_cast<void*>(GetProcAddress(module, "sceKernelMapNamedSystemFlexibleMemory"));
#else
auto* symbol = dlsym(RTLD_DEFAULT, "sceKernelMapNamedSystemFlexibleMemory");
#endif
if (!symbol) {
std::fprintf(stderr, "sceKernelMapNamedSystemFlexibleMemory is not exported by libkernel\n");
std::abort();
}
return reinterpret_cast<MapSystemFlexible>(symbol);
}
static std::size_t Available() {
std::size_t available = 0;
Require(sceKernelAvailableFlexibleMemorySize(&available) == 0);
return available;
}
int main() {
mapSystemFlexible = Resolve();
constexpr std::size_t length = PS5_PAGE_SIZE * 4;
const std::size_t before = Available();
void* mapped = nullptr;
Require(mapSystemFlexible(&mapped, length, 3, 0, "system mapping") == 0 && mapped != nullptr);
auto* bytes = static_cast<volatile unsigned char*>(mapped);
bytes[0] = 17;
bytes[length - 1] = 29;
Require(bytes[0] == 17 && bytes[length - 1] == 29);
VirtualQueryInfo info{};
Require(sceKernelVirtualQuery(mapped, 0, &info, sizeof(info)) == 0);
Require(std::strcmp(info.name, "system mapping") == 0 && info.protection == 3 && info.is_direct == 0);
Require(Available() == before);
void* game = nullptr;
Require(sceKernelMapNamedFlexibleMemory(&game, length, 3, 0, "game mapping") == 0 && game != nullptr);
Require(Available() == before - length);
void* replacement = static_cast<unsigned char*>(game) + PS5_PAGE_SIZE;
void* fixed = replacement;
Require(mapSystemFlexible(&fixed, PS5_PAGE_SIZE, 3, 0x10, "system fixed") == 0 && fixed == replacement);
Require(Available() == before - length + PS5_PAGE_SIZE);
Require(sceKernelMunmap(reinterpret_cast<std::uint64_t>(game), length) == 0);
Require(Available() == before);
void* invalid = nullptr;
Require(mapSystemFlexible(&invalid, 0, 3, 0, "empty") == SCE_KERNEL_ERROR_EINVAL && invalid == nullptr);
Require(mapSystemFlexible(&invalid, PS5_PAGE_SIZE + 1, 3, 0, "unaligned") == SCE_KERNEL_ERROR_EINVAL && invalid == nullptr);
Require(mapSystemFlexible(&invalid, PS5_PAGE_SIZE + 1, 3, 0, nullptr) == SCE_KERNEL_ERROR_EINVAL && invalid == nullptr);
void* longest = nullptr;
Require(mapSystemFlexible(&longest, PS5_PAGE_SIZE, 3, 0, "abcdefghijklmnopqrstuvwxyz01234") == 0 && longest != nullptr);
Require(sceKernelVirtualQuery(longest, 0, &info, sizeof(info)) == 0 && std::strcmp(info.name, "abcdefghijklmnopqrstuvwxyz01234") == 0);
Require(sceKernelMunmap(reinterpret_cast<std::uint64_t>(longest), PS5_PAGE_SIZE) == 0);
Require(sceKernelMunmap(reinterpret_cast<std::uint64_t>(mapped), length) == 0);
Require(Available() == before);
return 0;
}
+1
View File
@@ -126,6 +126,7 @@
- [sceKernelPrintBacktraceWithModuleInfo](../../../core/libs/prx/libkernel/Unimplemented.cpp) (libkernel) - imported by PS4 titles, which do not load without it, and added without an implementation: declared without parameters, every call throws
- [pthread_timedjoin_np](../../../core/libs/prx/libkernel/Pthread/Posix/Thread.cpp) (libkernel, NID `PkS44IGrDkM`) - the signature follows [ps5-payload-dev/sdk](https://github.com/ps5-payload-dev/sdk/blob/a7685ce8e97ce1f8a4f38253a41d54241c9124f1/include/freebsd/pthread_np.h#L71); deadline validation, absolute realtime deadlines, error precedence and join ownership follow [FreeBSD libthr](https://github.com/freebsd/freebsd-src/blob/d106002967eff712894ab8b5337bbbff106ce5e0/lib/libthr/thread/thr_join.c). The [public 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libkernel.c) lists this NID, but PS5 behavior and module/library versions have not been measured. Realtime is rechecked at most two seconds apart while waiting. After guest cleanup completes, final native thread teardown uses the existing unbounded join and may finish after the deadline; host teardown is not bounded by this implementation.
- [sceKernelMapNamedSystemFlexibleMemory](../../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) - the PS5 system budget and the `sceKernelVirtualQuery` record of a system map are unmeasured. It is not charged to the game flexible budget, as in [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/30f4170896ce974353a6f6486d0ecec179aec54b/src/core/libraries/kernel/memory.cpp#L354-L391), which charges only its flexible type; fpPS4 ignores `MAP_SYSTEM` and charges it. It reports as flexible, as in fpPS4; shadPS4 reports it as not flexible. Checks, alignment and placement are `sceKernelMapNamedFlexibleMemory`'s, so flag 0x8000 and protection 0x800 throw, unmeasured. A null name or one of 32 bytes or more throws, unmeasured: shadPS4 returns `EFAULT` or `ENAMETOOLONG` before mapping, [fpPS4](https://github.com/red-prig/fpPS4/blob/04cefd43e6fddd1ab033e7980cd356d14c964905/kernel/ps4_map_mm.pas#L1121-L1209) maps it unnamed. Closes when: measured on a PS5
### Functional