Files
AnyPS5/core/libs/tests/GuestMlock.cpp
T
al3w0f205 e03e6f7a4e feat(libkernel): add munlockall export
Follows mlockall (#4386) for XPSemu (PPSA97358, QEMU os-posix.c), which
imports munlockall (NpLBpgVV7PU) from libkernel.prx. Predicted stop:

  FAIL: unresolved ELF import NpLBpgVV7PU from libkernel.prx

The NID is computed with tools/nid_names.py and matches aerolib.

Follows FreeBSD 11.4 sys/vm/vm_mmap.c sys_munlockall
(8c639945a44588f6cfdddfb057bb5aad88735da0): checks PRIV_VM_MUNLOCK,
clears wire state and returns 0. To match, munlockall unlocks ranges
locked by mlock (through VirtualUnlock on Windows, tolerating
ERROR_NOT_LOCKED, and munlockall on Linux).
2026-10-14 18:07:34 -05:00

166 lines
5.6 KiB
C++

#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <limits>
#include <stdexcept>
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#endif
#if defined(__linux__)
#include <fstream>
#include <string>
#endif
extern "C" {
int APS5_VABI sceKernelMapFlexibleMemory(void**, std::size_t, int, int);
int APS5_VABI sceKernelReserveVirtualRange(void**, std::size_t, int, std::size_t);
int APS5_VABI sceKernelMunmap(void*, std::size_t);
int APS5_VABI mlock_nid_postfix(const void*, std::size_t);
int APS5_VABI munlock_nid_postfix(const void*, std::size_t);
int APS5_VABI mlockall_nid_postfix(int);
int APS5_VABI munlockall_nid_postfix();
int* APS5_VABI __error_nid_postfix();
}
static void Require(bool value) { if (!value) std::abort(); }
static void RequireLockAllInvalid(int flags) {
*__error_nid_postfix() = 0;
Require(mlockall_nid_postfix(flags) == -1 && *__error_nid_postfix() == 22);
}
static void RequireLockAllUnsupported(int flags) {
bool rejected = false;
try {
mlockall_nid_postfix(flags);
} catch (const std::runtime_error&) {
rejected = true;
}
Require(rejected);
}
#if defined(__linux__) && !defined(ANYPS5_ENABLE_ASAN)
#define HAS_LOCKED_KILOBYTES
static std::size_t LockedKilobytes() {
std::ifstream status("/proc/self/status");
std::string line;
while (std::getline(status, line)) {
if (line.rfind("VmLck:", 0) == 0) return std::strtoull(line.c_str() + 6, nullptr, 10);
}
return 0;
}
#endif
int main() {
constexpr int untouched = 12345;
constexpr std::size_t page = 0x4000;
#ifdef _WIN32
constexpr std::size_t length = 0x400000;
#else
constexpr std::size_t length = 0x10000;
#endif
void* mapped = nullptr;
Require(sceKernelMapFlexibleMemory(&mapped, length, 3, 0) == 0);
auto* bytes = static_cast<unsigned char*>(mapped);
*__error_nid_postfix() = untouched;
Require(mlock_nid_postfix(mapped, 0) == 0);
Require(*__error_nid_postfix() == untouched);
#ifdef HAS_LOCKED_KILOBYTES
const auto lockedBefore = LockedKilobytes();
#endif
Require(mlock_nid_postfix(bytes + 1, length - page) == 0);
#ifdef HAS_LOCKED_KILOBYTES
Require(LockedKilobytes() - lockedBefore == length / 1024);
#endif
Require(mlock_nid_postfix(mapped, length) == 0);
Require(mlock_nid_postfix(mapped, length) == 0);
bytes[length - 1] = 7;
*__error_nid_postfix() = untouched;
Require(munlock_nid_postfix(mapped, 0) == 0);
Require(*__error_nid_postfix() == untouched);
Require(munlock_nid_postfix(bytes + 1, length - page) == 0);
#ifdef HAS_LOCKED_KILOBYTES
Require(LockedKilobytes() == lockedBefore);
#elif defined(_WIN32)
Require(!VirtualUnlock(mapped, length) && GetLastError() == ERROR_NOT_LOCKED);
#endif
*__error_nid_postfix() = untouched;
Require(munlock_nid_postfix(mapped, length) == 0);
Require(*__error_nid_postfix() == untouched);
bytes[0] = 7;
Require(mlock_nid_postfix(mapped, page) == 0);
#ifdef HAS_LOCKED_KILOBYTES
Require(LockedKilobytes() - lockedBefore == page / 1024);
#endif
Require(munlock_nid_postfix(mapped, length) == 0);
#ifdef HAS_LOCKED_KILOBYTES
Require(LockedKilobytes() == lockedBefore);
#elif defined(_WIN32)
Require(!VirtualUnlock(mapped, page) && GetLastError() == ERROR_NOT_LOCKED);
#endif
Require(mlock_nid_postfix(mapped, page) == 0);
Require(munlock_nid_postfix(mapped, page) == 0);
#ifdef HAS_LOCKED_KILOBYTES
Require(LockedKilobytes() == lockedBefore);
#elif defined(_WIN32)
Require(!VirtualUnlock(mapped, page) && GetLastError() == ERROR_NOT_LOCKED);
#endif
Require(mlock_nid_postfix(mapped, page) == 0);
#ifdef HAS_LOCKED_KILOBYTES
Require(LockedKilobytes() - lockedBefore == page / 1024);
#endif
*__error_nid_postfix() = untouched;
Require(munlockall_nid_postfix() == 0);
Require(*__error_nid_postfix() == untouched);
#ifdef HAS_LOCKED_KILOBYTES
Require(LockedKilobytes() == lockedBefore);
#elif defined(_WIN32)
Require(!VirtualUnlock(mapped, page) && GetLastError() == ERROR_NOT_LOCKED);
#endif
*__error_nid_postfix() = 0;
Require(mlock_nid_postfix(reinterpret_cast<void*>(std::numeric_limits<std::uintptr_t>::max() - page + 1), page * 2) == -1);
Require(*__error_nid_postfix() == 22);
*__error_nid_postfix() = 0;
Require(munlock_nid_postfix(reinterpret_cast<void*>(std::numeric_limits<std::uintptr_t>::max() - page + 1), page * 2) == -1);
Require(*__error_nid_postfix() == 22);
void* reserved = nullptr;
Require(sceKernelReserveVirtualRange(&reserved, page, 0, 0) == 0);
#ifndef ANYPS5_ENABLE_ASAN
*__error_nid_postfix() = 0;
Require(mlock_nid_postfix(reserved, page) == -1 && *__error_nid_postfix() == 12);
#endif
*__error_nid_postfix() = untouched;
Require(munlock_nid_postfix(reserved, page) == 0);
Require(*__error_nid_postfix() == untouched);
Require(sceKernelMunmap(reserved, page) == 0);
Require(sceKernelMunmap(mapped, length) == 0);
#ifndef ANYPS5_ENABLE_ASAN
*__error_nid_postfix() = 0;
Require(mlock_nid_postfix(mapped, page) == -1 && *__error_nid_postfix() == 12);
*__error_nid_postfix() = 0;
Require(munlock_nid_postfix(mapped, page) == -1 && *__error_nid_postfix() == 12);
#endif
RequireLockAllInvalid(0);
RequireLockAllInvalid(4);
RequireLockAllInvalid(1 | 4);
RequireLockAllInvalid(-1);
RequireLockAllUnsupported(1);
RequireLockAllUnsupported(2);
RequireLockAllUnsupported(1 | 2);
*__error_nid_postfix() = untouched;
Require(munlockall_nid_postfix() == 0);
Require(*__error_nid_postfix() == untouched);
return 0;
}