mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-07 13:39:55 +00:00
190 lines
7.3 KiB
C++
190 lines
7.3 KiB
C++
#include "prx/libkernel/KernelErrors.hpp"
|
|
#include "prx/libkernel/include/Api.hpp"
|
|
#include "prx/libkernel/Time/include/Time.hpp"
|
|
#include "prx/libc/include/Api.hpp"
|
|
#include "SceTypes.hpp"
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <initializer_list>
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#else
|
|
#include <sys/mman.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
using Nanosleep = int (APS5_VABI *)(const KernelTimespec*, KernelTimespec*);
|
|
|
|
static constexpr int SCE_OK = 0;
|
|
|
|
static constexpr int GUEST_EACCES = 13;
|
|
static constexpr int GUEST_EFAULT = 14;
|
|
static constexpr int GUEST_EINVAL = 22;
|
|
static constexpr int GUEST_CLOCK_MONOTONIC = 4;
|
|
|
|
static constexpr std::int64_t NANOS_PER_SECOND = 1000000000LL;
|
|
static constexpr std::int64_t NANOS_PER_MILLISECOND = 1000000LL;
|
|
static constexpr std::int64_t SLEEP_NANOS = 50 * NANOS_PER_MILLISECOND;
|
|
static constexpr std::int64_t EARLY_WAKE_MARGIN_NANOS = 5 * NANOS_PER_MILLISECOND;
|
|
static constexpr std::int64_t NO_SLEEP_LIMIT_NANOS = 2 * NANOS_PER_SECOND;
|
|
|
|
static void Require(bool value) { if (!value) std::abort(); }
|
|
|
|
static std::int64_t MonotonicNanos() {
|
|
KernelTimespec time{-1, -1};
|
|
Require(clock_gettime_nid_postfix(GUEST_CLOCK_MONOTONIC, &time) == SCE_OK);
|
|
return time.tv_sec * NANOS_PER_SECOND + time.tv_nsec;
|
|
}
|
|
|
|
static void PosixRejects(Nanosleep sleep, const KernelTimespec* request, int error) {
|
|
*__error_nid_postfix() = GUEST_EACCES;
|
|
Require(sleep(request, nullptr) == -1);
|
|
Require(*__error_nid_postfix() == error);
|
|
}
|
|
|
|
static void PosixRejectsInvalidRequests(Nanosleep sleep) {
|
|
const KernelTimespec negativeNanos{0, -1};
|
|
const KernelTimespec wholeSecondOfNanos{0, NANOS_PER_SECOND};
|
|
const KernelTimespec negativeBoth{-1, -1};
|
|
PosixRejects(sleep, &negativeNanos, GUEST_EINVAL);
|
|
PosixRejects(sleep, &wholeSecondOfNanos, GUEST_EINVAL);
|
|
PosixRejects(sleep, &negativeBoth, GUEST_EINVAL);
|
|
PosixRejects(sleep, nullptr, GUEST_EFAULT);
|
|
}
|
|
|
|
static void SceRejectsInvalidRequests() {
|
|
const KernelTimespec negativeNanos{0, -1};
|
|
const KernelTimespec wholeSecondOfNanos{0, NANOS_PER_SECOND};
|
|
const KernelTimespec negativeBoth{-1, -1};
|
|
Require(sceKernelNanosleep(&negativeNanos, nullptr) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(sceKernelNanosleep(&wholeSecondOfNanos, nullptr) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(sceKernelNanosleep(&negativeBoth, nullptr) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(sceKernelNanosleep(nullptr, nullptr) == SCE_KERNEL_ERROR_EFAULT);
|
|
}
|
|
|
|
static void ReturnsAtOnce(Nanosleep sleep, KernelTimespec request) {
|
|
const std::int64_t start = MonotonicNanos();
|
|
*__error_nid_postfix() = GUEST_EACCES;
|
|
Require(sleep(&request, nullptr) == SCE_OK);
|
|
Require(*__error_nid_postfix() == GUEST_EACCES);
|
|
Require(MonotonicNanos() - start < NO_SLEEP_LIMIT_NANOS);
|
|
}
|
|
|
|
static void NegativeSecondsDoNotSleep(Nanosleep sleep) {
|
|
ReturnsAtOnce(sleep, {-1, 0});
|
|
ReturnsAtOnce(sleep, {-1, NANOS_PER_SECOND - 1});
|
|
ReturnsAtOnce(sleep, {INT64_MIN, 0});
|
|
ReturnsAtOnce(sleep, {0, 0});
|
|
}
|
|
|
|
static void SleepsForTheRequest(Nanosleep sleep) {
|
|
const KernelTimespec request{0, SLEEP_NANOS};
|
|
const std::int64_t start = MonotonicNanos();
|
|
*__error_nid_postfix() = GUEST_EACCES;
|
|
Require(sleep(&request, nullptr) == SCE_OK);
|
|
Require(*__error_nid_postfix() == GUEST_EACCES);
|
|
Require(MonotonicNanos() - start >= SLEEP_NANOS - EARLY_WAKE_MARGIN_NANOS);
|
|
}
|
|
|
|
static void CompletedSleepLeavesTheRemainingTimeAlone(Nanosleep sleep) {
|
|
const KernelTimespec request{0, NANOS_PER_MILLISECOND};
|
|
KernelTimespec remaining{7, 9};
|
|
Require(sleep(&request, &remaining) == SCE_OK);
|
|
Require(remaining.tv_sec == 7 && remaining.tv_nsec == 9);
|
|
KernelTimespec shared = request;
|
|
for (int i = 0; i < 3; ++i) {
|
|
Require(sleep(&shared, &shared) == SCE_OK);
|
|
Require(shared.tv_sec == request.tv_sec && shared.tv_nsec == request.tv_nsec);
|
|
}
|
|
const KernelTimespec invalid{0, NANOS_PER_SECOND};
|
|
remaining = {7, 9};
|
|
Require(sleep(&invalid, &remaining) != SCE_OK);
|
|
Require(remaining.tv_sec == 7 && remaining.tv_nsec == 9);
|
|
}
|
|
|
|
class GuardedRequest {
|
|
public:
|
|
GuardedRequest() {
|
|
#ifdef _WIN32
|
|
SYSTEM_INFO info{};
|
|
GetSystemInfo(&info);
|
|
pageSize = info.dwPageSize;
|
|
memory = static_cast<unsigned char*>(VirtualAlloc(nullptr, 2 * pageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
|
Require(memory != nullptr);
|
|
#else
|
|
const long size = sysconf(_SC_PAGESIZE);
|
|
Require(size > 0);
|
|
pageSize = static_cast<std::size_t>(size);
|
|
void* mapping = mmap(nullptr, 2 * pageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
Require(mapping != MAP_FAILED);
|
|
memory = static_cast<unsigned char*>(mapping);
|
|
#endif
|
|
}
|
|
~GuardedRequest() {
|
|
#ifdef _WIN32
|
|
Require(VirtualFree(memory, 0, MEM_RELEASE) != 0);
|
|
#else
|
|
Require(munmap(memory, 2 * pageSize) == 0);
|
|
#endif
|
|
}
|
|
KernelTimespec* AtBoundary(std::size_t readableBytes) {
|
|
return reinterpret_cast<KernelTimespec*>(memory + pageSize - readableBytes);
|
|
}
|
|
void Protect(bool readable) {
|
|
#ifdef _WIN32
|
|
DWORD oldProtect{};
|
|
Require(VirtualProtect(memory + pageSize, pageSize, readable ? PAGE_READWRITE : PAGE_NOACCESS, &oldProtect) != 0);
|
|
#else
|
|
Require(mprotect(memory + pageSize, pageSize, readable ? PROT_READ | PROT_WRITE : PROT_NONE) == 0);
|
|
#endif
|
|
}
|
|
private:
|
|
unsigned char* memory{};
|
|
std::size_t pageSize{};
|
|
};
|
|
|
|
static void RejectsUnreadableRequests(Nanosleep sleep, bool sce) {
|
|
GuardedRequest mapping;
|
|
for (const std::size_t readableBytes : {std::size_t{0}, sizeof(std::int64_t)}) {
|
|
auto* request = mapping.AtBoundary(readableBytes);
|
|
const KernelTimespec zero{};
|
|
std::memcpy(request, &zero, sizeof(zero));
|
|
Require(sleep(request, nullptr) == SCE_OK);
|
|
mapping.Protect(false);
|
|
KernelTimespec remaining{7, 9};
|
|
*__error_nid_postfix() = GUEST_EACCES;
|
|
const int result = sleep(request, &remaining);
|
|
Require(result == (sce ? SCE_KERNEL_ERROR_EFAULT : -1));
|
|
Require(*__error_nid_postfix() == (sce ? GUEST_EACCES : GUEST_EFAULT));
|
|
Require(remaining.tv_sec == 7 && remaining.tv_nsec == 9);
|
|
auto* inside = mapping.AtBoundary(sizeof(KernelTimespec));
|
|
std::memcpy(inside, &zero, sizeof(zero));
|
|
Require(sleep(inside, nullptr) == SCE_OK);
|
|
mapping.Protect(true);
|
|
std::memcpy(request, &zero, sizeof(zero));
|
|
Require(sleep(request, nullptr) == SCE_OK);
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
RejectsUnreadableRequests(nanosleep_nid_postfix, false);
|
|
RejectsUnreadableRequests(_nanosleep_nid_postfix, false);
|
|
RejectsUnreadableRequests(sceKernelNanosleep, true);
|
|
PosixRejectsInvalidRequests(nanosleep_nid_postfix);
|
|
PosixRejectsInvalidRequests(_nanosleep_nid_postfix);
|
|
SceRejectsInvalidRequests();
|
|
|
|
NegativeSecondsDoNotSleep(nanosleep_nid_postfix);
|
|
NegativeSecondsDoNotSleep(_nanosleep_nid_postfix);
|
|
NegativeSecondsDoNotSleep(sceKernelNanosleep);
|
|
|
|
SleepsForTheRequest(nanosleep_nid_postfix);
|
|
SleepsForTheRequest(_nanosleep_nid_postfix);
|
|
SleepsForTheRequest(sceKernelNanosleep);
|
|
|
|
CompletedSleepLeavesTheRemainingTimeAlone(nanosleep_nid_postfix);
|
|
CompletedSleepLeavesTheRemainingTimeAlone(_nanosleep_nid_postfix);
|
|
CompletedSleepLeavesTheRemainingTimeAlone(sceKernelNanosleep);
|
|
}
|