Files
AnyPS5/core/libs/tests/GuestCurrentCpu.cpp
T
milescovielloandClaude Opus 5.5 6508231c0e fix(libkernel): measure the TSC frequency on one CPU
sceKernelGetTscFrequency times 20 ms of TSC ticks against the monotonic
clock. A thread that moves to another CPU during the sleep reads its end
value from another TSC. Where the TSCs of the CPUs differ, as on a Windows
VM whose vCPU 0 counts about 3.8 billion ticks ahead of the others, the
frequency came out as 0 or as 180 GHz. Pin the calibrating thread to the
CPU it runs on and restore its affinity afterwards.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-25 02:24:56 +00:00

63 lines
2.5 KiB
C++

#include "prx/libkernel/include/Api.hpp"
#include "prx/libkernel/Time/include/Time.hpp"
#include "SceTypes.hpp"
#include "prx/libkernel/Pthread/include/Pthread.hpp"
#include <array>
#include <bit>
#include <cstdlib>
static constexpr int SCE_OK = 0;
static constexpr unsigned MAX_HOST_CPUS = 256;
static constexpr int CALLS_PER_THREAD = 64;
static void Require(bool value) { if (!value) std::abort(); }
static bool InMask(int cpu, KernelCpumask mask) {
return cpu >= 0 && cpu < 64 && (mask >> cpu & 1) != 0;
}
struct PinnedRun {
KernelCpumask affinity;
bool inside;
};
static void* APS5_VABI ReportWhilePinned(void* arg) {
auto* run = static_cast<PinnedRun*>(arg);
run->inside = scePthreadSetaffinity(scePthreadSelf(), run->affinity) == SCE_OK;
for (int call = 0; call < CALLS_PER_THREAD && run->inside; ++call)
run->inside = InMask(sceKernelGetCurrentCpu(), run->affinity);
return nullptr;
}
int main() {
const KernelCpumask available = sceKernelGetAvailableCpumask();
Require(sceKernelGetMainSocId() == 0 && sceKernelGetCpumode() == 0);
Require(sceKernelGetCpuFrequency() > 0 && static_cast<std::uint64_t>(sceKernelGetCpuFrequency()) == sceKernelGetTscFrequency());
Require(sceKernelGetTscFrequency() >= 100000000u && sceKernelGetTscFrequency() <= 10000000000u);
const std::array<KernelCpumask, 8> affinities{available, 0x1FFB, 0x3, 0x3F, 0x1000, 0x1, 0xFFFF, 0x10000};
for (const KernelCpumask affinity : affinities) {
const KernelCpumask expected = (affinity & available) != 0 ? affinity & available : available;
KernelCpumask reached = 0;
for (unsigned hostCpu = 0; hostCpu < MAX_HOST_CPUS; ++hostCpu) {
const int cpu = GuestCpuFromHost(hostCpu, affinity);
Require(InMask(cpu, expected));
Require(GuestCpuFromHost(hostCpu, affinity) == cpu);
reached |= KernelCpumask{1} << cpu;
if (hostCpu + 1 == static_cast<unsigned>(std::popcount(expected))) Require(reached == expected);
}
Require(reached == expected);
}
for (int call = 0; call < CALLS_PER_THREAD; ++call) Require(InMask(sceKernelGetCurrentCpu(), available));
for (KernelCpumask cpu = 1; cpu != 0; cpu <<= 1) {
if ((available & cpu) == 0) continue;
PinnedRun run{cpu, false};
Pthread thread = nullptr;
Require(scePthreadCreate(&thread, nullptr, ReportWhilePinned, &run, nullptr) == SCE_OK);
Require(scePthreadJoin(thread, nullptr) == SCE_OK);
Require(run.inside);
}
}