#include "SceTypes.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include #include #include #include #include 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::min(), std::numeric_limits::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 unsupported : {1, 2}) { GuestItimerval output{{11, 22}, {33, 44}}; const auto saved = output; bool threw = false; try { getitimer_nid_postfix(unsupported, &output); } catch (const std::runtime_error&) { threw = true; } Require(threw && std::memcmp(&output, &saved, sizeof(output)) == 0, "unsupported CPU timer reported fabricated state"); } const auto ignore = reinterpret_cast(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 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::max()); Require(ReadRemaining() > static_cast(std::numeric_limits::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); }