Merge pull request #12577 from iAbdulrrahman/feat/libkernel-sys-is-ps4-process

feat(libkernel_sys): implement sceKernelIsPs4Process query
This commit is contained in:
Adrià authored and GitHub committed 2026-10-30 03:03:26 +01:00
commit acf46d4ff8
4 files changed
+55 -9

No files matched your search

@@ -1333,11 +1333,6 @@ int APS5_VABI sceKernelIsNeoMode(void) {
return (attribute & PARAM_SFO_ATTRIBUTE_SUPPORT_NEO_MODE) != 0 ? 1 : 0;
}
int APS5_VABI sceKernelIsPs4Process(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceKernelJitGetSharedMemoryInfo(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
+17
View File
@@ -28,6 +28,7 @@
#include "prx/libc/include/General.hpp"
#include "prx/libc/include/ApplicationHeap.hpp"
#include "prx/libkernel/KernelErrors.hpp"
#include "prx/libkernel/AppMetadata/include/AppMetadata.hpp"
#include "prx/libc/include/FilesystemError.hpp"
#include "prx/libkernel/KernelErrors.hpp"
#include "prx/libkernel/Pthread/include/Pthread.hpp"
@@ -426,6 +427,22 @@ int APS5_VABI sceKernelIsKratos(void) {
return 0;
}
bool IsPs4Process() {
try {
const auto titleId = GetAppTitleId_nid_postfix();
const std::string_view id(titleId.value, strnlen(titleId.value, sizeof(titleId.value)));
return id.starts_with("CUSA");
} catch (const std::runtime_error&) {
return false;
} catch (const std::length_error&) {
return false;
}
}
int APS5_VABI sceKernelIsPs4Process(void) {
return IsPs4Process() ? 1 : 0;
}
int APS5_VABI get_authinfo_nid_postfix(int pid, KernelAuthInfo* info) {
if (info == nullptr) return static_cast<int>(Fail(GuestEperm));
if (pid != 0 && pid != getpid_nid_postfix()) return static_cast<int>(Fail(GuestEsrch));
@@ -6,6 +6,11 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <random>
#include <string>
#include <string_view>
namespace {
@@ -19,7 +24,7 @@ void Require(bool condition, const char* message) {
}
}
void CheckConsoleKind() {
void CheckConsoleKind(bool ps4) {
Require(sceKernelIsCEX() == 1, "the console must be reported as a retail unit");
Require(sceKernelIsDevKit() == 0, "the console must not be reported as a development kit");
Require(sceKernelIsGenuineCEX() == 1, "the console must be reported as a genuine retail unit");
@@ -28,6 +33,11 @@ void CheckConsoleKind() {
Require(sceKernelIsTestKit() == 0, "the console must not be reported as a test kit");
Require(sceKernelIsProspero() == 1, "the architecture must be reported as Prospero");
Require(sceKernelIsKratos() == 0, "the architecture must not be reported as Kratos");
if (ps4) {
Require(sceKernelIsPs4Process() == 1, "a CUSA title must be reported as a PS4 process");
} else {
Require(sceKernelIsPs4Process() == 0, "the default process must not be reported as a PS4 process");
}
}
void CheckAuthInfo() {
@@ -69,9 +79,33 @@ void CheckOpenPsId() {
}
int main() {
CheckConsoleKind();
int main(int argc, char** argv) {
if (argc == 2 && std::string_view(argv[1]) == "ps4-child") {
CheckConsoleKind(true);
return 0;
}
CheckConsoleKind(false);
CheckAuthInfo();
CheckOpenPsId();
const auto directory = std::filesystem::temp_directory_path() / ("ps4-identity-test-" + std::to_string(std::random_device{}()));
std::filesystem::create_directories(directory / "app0" / "sce_sys");
struct Entry { std::uint16_t key, format; std::uint32_t length, maximum, offset; };
const std::uint32_t header[5] = {0x46535000, 0x0101, 52, 72, 2};
const Entry entries[2] = {{0, 0x0204, 8, 8, 0}, {6, 0x0204, 10, 12, 8}};
{
std::ofstream file(directory / "app0/sce_sys/param.sfo", std::ios::binary);
file.write(reinterpret_cast<const char*>(header), sizeof(header));
file.write(reinterpret_cast<const char*>(entries), sizeof(entries));
file << std::string("TITLE\0TITLE_ID\0\0\0\0\0\0", 20) << std::string("Example\0", 8) << "CUSA00000" << std::string("\0\0\0", 3);
}
#ifdef _WIN32
const std::string command = "\"cd /d \"" + directory.string() + "\" && \"" + argv[0] + "\" ps4-child\"";
#else
const std::string command = "cd \"" + directory.string() + "\" && \"" + argv[0] + "\" ps4-child";
#endif
const int status = std::system(command.c_str());
std::filesystem::remove_all(directory);
Require(status == 0, "the PS4 child process must succeed");
return 0;
}
+1 -1
View File
@@ -368,7 +368,7 @@
- [sigreturn](../../../core/libs/prx/libkernel_sys/Export.Discovery.cpp) (libkernel_sys) follows FreeBSD 11.4's amd64 [`sys_sigreturn`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/amd64/amd64/machdep.c#L473-L576) on `setcontext`'s `ucontext_t`: a null context returns `EFAULT`, and `mc_flags` outside `_MC_FLAG_MASK` or an `rflags` differing from the current flags outside `PSL_USERCHANGE` return `EINVAL`; `mc_len` is not checked. As in [`set_fpcontext`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/amd64/amd64/machdep.c#L2349-L2370), `_MC_FPFMT_NODEV` keeps the FPU state, `_MC_FPOWNED_NONE` loads the initial one, the FPU and PCB owners load `mc_fpstate`, and other values return `EINVAL`. The rest is restored as by `setcontext`, which keeps the `%fs` and `%gs` bases FreeBSD would load. A `%cs` that is not a user selector (FreeBSD also raises `SIGBUS`) and `_MC_HASFPXSTATE` throw
- [sceKernelGetModuleInfoInternal, sceKernelGetModuleListInternal and sceKernelLoadStartModuleForSysmodule](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) answer for every loaded module, system modules included. `sceKernelGetModuleInfoInternal` fills the 0x1A8-byte `SceKernelModuleInfoEx` of a module handle the way `sceKernelGetModuleInfoFromAddr` does for an address, with [shadPS4's checks](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/kernel/process.cpp#L243-L258) in their order: `EFAULT` for a null pointer, `EINVAL` when `st_size` is not 0x1A8, `ESRCH` for an unknown handle. `sceKernelGetModuleListInternal` returns the same list as `sceKernelGetModuleList`, as [fpPS4](https://github.com/red-prig/fpPS4/blob/04cefd4/kernel/ps4_libkernel.pas#L852-L858) does; fpPS4 marks that as a guess, and neither is checked on a PS5. `sceKernelLoadStartModuleForSysmodule` forwards to `sceKernelLoadStartModule`
- [chroot](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) follows FreeBSD 11.4's [`sys_chroot`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/vfs_syscalls.c#L776-L807) and [`pwd_chroot`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_descrip.c#L3059-L3085) for a root caller: `EFAULT` for a null path, `ENOENT` for an empty or missing one, `ENAMETOOLONG` for a path of 1024 bytes or more or a component over 255, and `ENOTDIR` for a path that is not a directory or passes through a file. A directory that is the guest root (`/`, `/..`, or `.` after `fchdir` to a descriptor of `/`) succeeds and changes nothing, as the root stays the same directory; the guest is taken never to have changed its root, so open directory descriptors do not make it fail with `EPERM` (`kern.chroot_allow_open_directories` 1). Any other directory throws, naming the path: the guest path resolver has a fixed root and mount aliases (`/app0`, `/savedata0`) that do not move with it. On the console a title starts in a sandbox root, where `pwd_chroot` refuses with `EPERM` while a directory is open; whether that holds for titles, and whether a title holds `PRIV_VFS_CHROOT`, are unverified. Porpoise (PPSA99764) weak-imports it and calls it only as `fchdir` to its saved descriptor of `/` then `chroot(".")`, to put its sandbox root back after a jailbreak daemon replaced it ([`put_root_back`](https://github.com/elripalda/Porpoise-Dolphin-Emulator-for-PS5/blob/2ed1ab5a06995aa198f7bccd4da0f90c3fc29d39/src/porpoise_jailbreak.cpp#L303-L331))
- [Console identity](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) - `get_authinfo` errors and capability bits remain unverified on PS5; they follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/396c2a09a38d22eaa02aab32c1967263dceab95f/src/core/libraries/kernel/kernel.cpp), leaving the authority ID and other capability, attribute and credential words zero. The host models a retail unit (`IsCEX` 1, `IsGenuineCEX` 1, `IsDevKit` 0, `IsGenuineDevKit` 0, `IsTestKit` 0, `IsGenuineTestKit` 0) and reports Prospero architecture (`IsProspero` 1, `IsKratos` 0). The system OpenPSID reuses the ordinary ID; firmware 13.42 reads a separate `machdep.openpsid_for_sys`, whose value is unknown. Null returns positive 22 per the [module report](https://github.com/boykopovar/AnyPS5/pull/10524#issuecomment-6393566914).
- [Console identity](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) - `get_authinfo` errors and capabilities are unverified on PS5, following [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/396c2a09a38d22eaa02aab32c1967263dceab95f/src/core/libraries/kernel/kernel.cpp) with zero authority and credential words. The host models a retail unit (`IsCEX` 1, `IsGenuineCEX` 1, `IsDevKit` 0, `IsGenuineDevKit` 0, `IsTestKit` 0, `IsGenuineTestKit` 0) and Prospero (`IsProspero` 1, `IsKratos` 0). `sceKernelIsPs4Process` returns 1 when the title ID starts with `CUSA` and 0 otherwise, as `libSceRtc` and `libSceConvertKeycode` tell PS4 titles apart; this mapping follows the PS4 title-ID convention and is unmeasured on a console, since BunnyFl's [13.42 report](https://github.com/boykopovar/AnyPS5/pull/10959#issuecomment-6404011201) only mentions the export returning 0 in passing. System OpenPSID reuses the ordinary ID; firmware 13.42 reads a separate `machdep.openpsid_for_sys` of unknown value. Null returns positive 22 per the [module report](https://github.com/boykopovar/AnyPS5/pull/10524#issuecomment-6393566914).
- [kldfind, kldfirstmod, kldnext and kldunload](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) follow FreeBSD 11.4's linker calls ([sys/kern/kern_linker.c](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/kern_linker.c)): kldfind validates the module name with CopyPath (EFAULT on null or unreadable pointers, ENAMETOOLONG for paths of 1024 bytes or more) and returns ENOENT for any module name; on FreeBSD the kernel itself is linker file 1, and AnyPS5 models an empty file list, so kldnext(0) returns 0 and kldfind("kernel") fails; kldnext returns ENOENT for negative or positive fileids; kldfirstmod returns ENOENT for any fileid; and kldunload returns EPERM as sandboxed/jailed titles lack PRIV_KLD_UNLOAD. Dynamic kernel linker module lookup and management remain unverified on hardware
- [kldstat](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) never succeeds: no linker file is listed here, as for `kldnext`, so the kernel's own file (id 1 on FreeBSD) and the modules a console preloads are missing, every file id gets `ENOENT` and the structure is never written. The checks before the lookup follow FreeBSD 11.4's [`sys_kldstat`](https://github.com/freebsd/freebsd-src/blob/releng/11.4/sys/kern/kern_linker.c#L1228-L1250): only the `version` field is read (`EFAULT` when it cannot be), and a version other than the size of `struct kld_file_stat_1` (1056 bytes on amd64) or `struct kld_file_stat` (2080) gets `EINVAL`. Console behaviour is unverified
- [fchown](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) - ownership is not modelled: the guest's own owner and group (0) and the unchanged value (-1) are accepted, a descriptor that is not open fails with `EBADF`, and any other owner or group throws, naming it. The check reads the descriptor with `fstat`, ignoring the CRT's invalid-parameter handler on Windows as `DescriptorControl` does. The console's ownership rules are unverified. Morrowind (`mshivam019/morrowind-ps5`) imports it