Files
AnyPS5/core/libs/tests/GuestGetitimer.cpp
T
milescovielloandClaude Opus 5.5 9f704dc1eb fix(libkernel): read ITIMER_VIRTUAL and ITIMER_PROF as disarmed in getitimer
setitimer refuses to arm the CPU interval timers and already reads and
disarms them as zero, so they are never armed. getitimer threw for them
instead of reporting that state, as FreeBSD's kern_getitimer does for a
timer that is not set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 06:17:58 +00:00

97 lines
4.4 KiB
C++

#include "SceTypes.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <array>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <thread>
struct GuestItimerval {
KernelTimeval interval;
KernelTimeval value;
};
static_assert(sizeof(GuestItimerval) == 32 && offsetof(GuestItimerval, value) == 16);
extern "C" {
int APS5_VABI getitimer_nid_postfix(int, GuestItimerval*);
unsigned int APS5_VABI GuestAlarm_nid_no_patch(unsigned int);
int* APS5_VABI __error_nid_postfix();
using Handler = void (APS5_VABI *)(int);
Handler APS5_VABI signal_nid_postfix(int, Handler);
}
static void Require(bool value, const char* message) {
if (!value) {
std::fprintf(stderr, "%s\n", message);
std::abort();
}
}
static std::int64_t ReadRemaining() {
struct {
std::uint64_t before;
GuestItimerval timer;
std::uint64_t after;
} guarded;
std::memset(&guarded, 0xA5, sizeof(guarded));
*__error_nid_postfix() = 12345;
Require(getitimer_nid_postfix(0, &guarded.timer) == 0, "getitimer failed");
Require(*__error_nid_postfix() == 12345, "getitimer changed errno on success");
Require(guarded.before == 0xA5A5A5A5A5A5A5A5ull && guarded.after == guarded.before, "getitimer wrote outside itimerval");
Require(guarded.timer.interval.tv_sec == 0 && guarded.timer.interval.tv_usec == 0, "alarm acquired a repeat interval");
Require(guarded.timer.value.tv_sec >= 0 && guarded.timer.value.tv_usec >= 0
&& guarded.timer.value.tv_usec < 1000000, "invalid timeval normalization");
return guarded.timer.value.tv_sec * 1000000 + guarded.timer.value.tv_usec;
}
int main() {
GuestAlarm_nid_no_patch(0);
Require(ReadRemaining() == 0, "inactive alarm is not zero");
for (int invalid : {-1, 3, std::numeric_limits<int>::min(), std::numeric_limits<int>::max()}) {
GuestItimerval output{{11, 22}, {33, 44}};
const auto saved = output;
Require(getitimer_nid_postfix(invalid, &output) == -1 && *__error_nid_postfix() == 22, "invalid timer did not report EINVAL");
Require(std::memcmp(&output, &saved, sizeof(output)) == 0, "invalid timer changed output");
Require(getitimer_nid_postfix(invalid, nullptr) == -1 && *__error_nid_postfix() == 22, "invalid timer validation order changed");
}
Require(getitimer_nid_postfix(0, nullptr) == -1 && *__error_nid_postfix() == 14, "null output did not report EFAULT");
for (int disarmed : {1, 2}) {
GuestItimerval output{{11, 22}, {33, 44}};
*__error_nid_postfix() = 12345;
Require(getitimer_nid_postfix(disarmed, &output) == 0, "CPU timer read failed");
Require(*__error_nid_postfix() == 12345, "CPU timer read changed errno");
Require(output.interval.tv_sec == 0 && output.interval.tv_usec == 0 && output.value.tv_sec == 0 && output.value.tv_usec == 0,
"a CPU timer that cannot be armed did not read as disarmed");
Require(getitimer_nid_postfix(disarmed, nullptr) == -1 && *__error_nid_postfix() == 14, "null CPU timer output did not report EFAULT");
}
const auto ignore = reinterpret_cast<Handler>(std::uintptr_t{1});
Require(signal_nid_postfix(14, ignore) == nullptr, "SIGALRM was not initially defaulted");
Require(GuestAlarm_nid_no_patch(30) == 0, "alarm was already active");
const auto first = ReadRemaining();
Require(first > 0 && first <= 30000000, "armed alarm is not visible");
std::this_thread::sleep_for(std::chrono::milliseconds(25));
Require(ReadRemaining() < first, "getitimer does not count down");
std::array<std::thread, 4> readers;
for (auto& reader : readers) reader = std::thread([] {
for (int i = 0; i < 100; ++i) Require(ReadRemaining() > 0, "reading consumed the alarm");
});
for (auto& reader : readers) reader.join();
Require(GuestAlarm_nid_no_patch(0) > 0, "getitimer cancelled the alarm");
Require(ReadRemaining() == 0, "cancelled alarm is not zero");
GuestAlarm_nid_no_patch(std::numeric_limits<unsigned int>::max());
Require(ReadRemaining() > static_cast<std::int64_t>(std::numeric_limits<int>::max()) * 1000000,
"long alarm was truncated to signed 32 bits");
GuestAlarm_nid_no_patch(1);
std::this_thread::sleep_for(std::chrono::milliseconds(1100));
Require(ReadRemaining() == 0, "expired alarm is not zero");
GuestAlarm_nid_no_patch(0);
}