Merge pull request #1477 from awake-devel/fix/current-cpu-in-guest-mask
fix(libkernel): report the current CPU within the thread's guest affinity
This commit is contained in:
commit
fffb830363
|
|
@ -339,6 +339,16 @@ if(BUILD_TESTING)
|
|||
set_tests_properties(guest_available_cpumask PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_test_executable(guest_current_cpu_tests ${LIBS_INCLUDE_DIR}/tests/GuestCurrentCpu.cpp)
|
||||
target_include_directories(guest_current_cpu_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_current_cpu_tests PRIVATE libkernel libc)
|
||||
configure_windows_unwind(guest_current_cpu_tests)
|
||||
add_test(NAME guest_current_cpu COMMAND guest_current_cpu_tests)
|
||||
set_tests_properties(guest_current_cpu PROPERTIES TIMEOUT 20)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_current_cpu PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_test_executable(guest_getrusage_tests ${LIBS_INCLUDE_DIR}/tests/GuestGetrusage.cpp)
|
||||
target_include_directories(guest_getrusage_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_getrusage_tests PRIVATE libkernel libc)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include "prx/libkernel/Time/include/TimedWait.hpp"
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
|
@ -63,6 +64,13 @@ struct PthreadSemPrivate {
|
|||
static constexpr KernelCpumask DEFAULT_THREAD_AFFINITY = 0x1FFF;
|
||||
static constexpr int DEFAULT_THREAD_PRIORITY = 700;
|
||||
|
||||
inline int GuestCpuFromHost(unsigned hostCpu, KernelCpumask threadAffinity) {
|
||||
KernelCpumask mask = threadAffinity & DEFAULT_THREAD_AFFINITY;
|
||||
if (mask == 0) mask = DEFAULT_THREAD_AFFINITY;
|
||||
for (auto skip = hostCpu % static_cast<unsigned>(std::popcount(mask)); skip != 0; --skip) mask &= mask - 1;
|
||||
return std::countr_zero(mask);
|
||||
}
|
||||
|
||||
struct PthreadAttrPrivate {
|
||||
void* stackAddress = nullptr;
|
||||
std::size_t _stacksize;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "prx/libc/include/ApplicationHeap.hpp"
|
||||
#include "prx/libc/include/Shutdown.hpp"
|
||||
#include "prx/libkernel/DirectMemory/DirectMemory.hpp"
|
||||
#include "prx/libkernel/Pthread/include/Pthread.hpp"
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cerrno>
|
||||
|
|
@ -110,6 +111,8 @@ struct GuestResourceUsage {
|
|||
std::int64_t ru_nivcsw;
|
||||
};
|
||||
|
||||
extern "C" Pthread APS5_VABI scePthreadSelf();
|
||||
|
||||
extern "C" {
|
||||
|
||||
// unknown data
|
||||
|
|
@ -157,13 +160,13 @@ int APS5_VABI sceKernelGetCurrentCpu(void) {
|
|||
throw std::system_error(GetLastError(), std::system_category(), "Reading processor group size");
|
||||
index += count;
|
||||
}
|
||||
return static_cast<int>(index);
|
||||
#else
|
||||
const int cpu = ::sched_getcpu();
|
||||
if (cpu < 0)
|
||||
throw std::system_error(errno, std::generic_category(), "Reading current processor");
|
||||
return cpu;
|
||||
const auto index = static_cast<unsigned>(cpu);
|
||||
#endif
|
||||
return GuestCpuFromHost(index, scePthreadSelf()->affinity.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
std::uint64_t APS5_VABI sceKernelGetGPI(void) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include "prx/libkernel/Pthread/include/Pthread.hpp"
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <cstdlib>
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI sceKernelGetCurrentCpu(void);
|
||||
KernelCpumask APS5_VABI sceKernelGetAvailableCpumask(void);
|
||||
Pthread APS5_VABI scePthreadSelf();
|
||||
int APS5_VABI scePthreadCreate(Pthread* thread, const PthreadAttr* attr, PthreadEntry entry, void* arg, const char* name);
|
||||
int APS5_VABI scePthreadJoin(Pthread thread, void** retval);
|
||||
int APS5_VABI scePthreadSetaffinity(Pthread thread, KernelCpumask mask);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -125,6 +125,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [sceKernelSyncOnAddressWait8 / sceKernelSyncOnAddressWait16 / sceKernelSyncOnAddressWait32 / sceKernelSyncOnAddressWait64](../../core/libs/prx/libkernel/SyncOnAddress/src/SyncOnAddress.cpp) (libkernel) - libc's `__std_atomic_wait_direct_8/16/32/64` pass a microsecond timeout or null and ignore the result; the by-value comparand, return values and the 2- and 8-byte alignment of Wait16 and Wait64 are assumed
|
||||
- [sceKernelAioSubmitReadCommandsMultiple / sceKernelAioSubmitWriteCommandsMultiple / sceKernelAioWaitRequests / sceKernelAioCancelRequest / sceKernelAioCancelRequests / sceKernelAioDeleteRequests](../../core/libs/prx/libkernel/Aio/src/Aio.cpp) (libkernel) - behaviour from shadPS4 (one id per request for the Multiple submits, cancel turns any request aborted and id 0 reports processing, wait mode 2 returns once one request completed); the 128 requests or ids per batch limit is from SharpProspero, with an unknown error, so larger batches throw; wait modes other than 1 (and) and 2 (or) throw; null pointers, negative counts and invalid ids return the codes the single-request siblings use, checked before any state is written
|
||||
- [sceKernelGetAvailableCpumask](../../core/libs/prx/libkernel/Pthread/src/Thread.cpp) (libkernel) - returns the default thread affinity, 0x1FFF (CPUs 0 to 12), as KytyPS5 does. Titles pin threads within it (PPSA02664 passes 0x1FFB, Hades II PPSA36082 0x3 and 0x3F); whether CPU 13 is available to a title is not confirmed
|
||||
- [sceKernelGetCurrentCpu](../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - guest affinities are not applied to host threads, so the host CPU index is mapped into the calling thread's affinity clipped to `sceKernelGetAvailableCpumask` (the available mask when they do not overlap): host CPU n reports the (n modulo the CPU count)-th CPU of that mask, so a thread pinned to one CPU reports that CPU, as a FreeBSD `cpuset` guarantees on the console. Which CPU the console reports for a thread allowed on several is not known; shadPS4 returns 0
|
||||
- [sceKernelMapNamedFlexibleMemoryInternal](../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) - flag 0x8000 is accepted and ignored; its meaning is not established. KytyPS5 (`GUEST_MAP_UNKNOWN_8000`) and sharpemu accept it without reading it. Every known caller is the SDK 9 `libc.prx` heap (Astro Bot PPSA21564, PPSA22520), which passes it with read/write protection and uses the mapping as an older `libc.prx` uses `sceKernelMapNamedFlexibleMemory` with flags 0
|
||||
- [sceKernelMtypeprotect](../../core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp) (libkernel) - sets the memory type of the direct mappings in the page-rounded range and of the direct memory behind them, as shadPS4 and prosper do (KytyPS5 ignores the type); the type is not validated, flexible pages in the range only get the protection, and a later map of the retyped direct memory reports the type it was allocated with. `sceKernelBatchMap2` operation 4 takes the same path with the entry type
|
||||
- [Guest arena](../../core/libs/prx/libc/src/GuestArena.cpp) (libc) - the application map area is assumed to end at 0xFC_0000_0000, as `SCE_KERNEL_APP_MAP_AREA_END_ADDR` does on the PS4. On Windows, host allocations made in the arena before libc loads stay, and a fixed guest mapping over one throws
|
||||
|
|
|
|||
Loading…
Reference in New Issue