mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-18 23:07:07 -05:00
PPSA34015 (EA SPORTS FC 27) stopped with "Joining guest thread: Access is denied": scePthreadJoin waited on a thread that another call released under it, and then waited on its closed handle. As FreeBSD 11.4's thr_join.c and thr_detach.c do with the joiner field, scePthreadDetach now returns EINVAL while a thread waits to join the target, a second concurrent scePthreadJoin returns EOPNOTSUPP, a cancelled join backs out, and a completed join leaves the thread detached.
163 KiB
163 KiB
libkernel technical debt
Silent stubs
- sceKernelGetSocPowerConsumption (libkernel) - returns
EINVALand writes nothing, assceKernelGetSocSensorTemperaturedoes when the host has no sensor: the host's package power is not read (Linux RAPLenergy_ujis readable by root only, and Windows has no general source). The prototype is not documented. PS5LM Plus (PPSA99581) passes a zeroed 128-byte buffer and, on success, reads its first 32-bit word as milliwatts, accepting 1000 to 350000; on failure it shows 0 W - scePthreadMutexattrSetprotocol (libkernel) - accepts 0 to 2 as a PS5 (firmware 13.60) does and returns
EINVALotherwise, but the protocol is not stored or applied: priority inheritance and protection are not modelled. On the console a null or uninitialized mutex attribute returnsEINVALfrom the SCE attribute calls,scePthreadMutexInittakes an uninitialized attribute as the default one, and a destroyed mutex returnsEINVALfromscePthreadMutexDestroy; a null mutex pointer crashed the payload and still throws - pthread_mutexattr_setprotocol, pthread_mutexattr_setprioceiling, pthread_mutexattr_getprioceiling (libkernel) - the protocol and the priority ceiling are stored and reported as in FreeBSD 11 libthr (the ceiling is set or read only under
PTHREAD_PRIO_PROTECT, and setting a protocol resets it to the highestSCHED_RRpriority). A null, uninitialized or destroyed attribute returnsEINVALbefore the other arguments are checked; this error behavior is unverified on the console. Mutexes do not apply the protocol or ceiling: a priority-inherit or priority-protect mutex locks like a normal one and does not change its owner's priority - sceKernelGetMainSocId and sceKernelGetCpumode (libkernel) both return 0, and sceKernelGetCpuFrequency returns the host TSC frequency measured for
sceKernelGetTscFrequency, not the console CPU clock. The PS5's SoC id and CPU mode are unknown; shadPS4's PS4 values are 0x710f10/0x740f30 and 0, 2 or 5 from the param.sfo CPU-mode flags. PPSA99203 only logs them ([console] main SoC id: result=%#x out=%#x,CPU: %llu Hz, cpumode %d), passing a pointer thatsceKernelGetMainSocIdleaves untouched - sceKernelEnableDmemAliasing (libkernel) returns 0 and changes nothing: direct memory can always be mapped more than once here, as
DoMapDirectin DirectMemory.cpp only checks that the physical pages are allocated, so there is no aliasing to turn on. shadPS4 (PS4) sets a flag there that lifts theEBUSYitssceKernelMapDirectMemory2, andsceKernelMapDirectMemoryfor SDK 2.50 and later, return for direct memory that is already mapped; that check is not applied here. The no-argumentintsignature follows shadPS4 and ProsperoEden (PPSA99008), which calls it before mapping the same direct memory again. Whether the PS5 refuses such a mapping without the call is unverified - sceKernelInternalMemoryGetAvailableSize (libkernel) reports 0 bytes and returns 0: what the console's internal memory pool holds and how large it is are unknown. The
int (size_t*)signature is from xUtilty (PS4). PPSA99203 calls it once besidesceKernelAvailableFlexibleMemorySizewhile starting its GS renderer and never reads the result (GS.cpp) - sceKernelIsThreadSanitizerEnabled (libkernel, NID
LzoM-wVLJDE) returns 0, assceKernelIsAddressSanitizerEnableddoes: a converted title never runs under the thread sanitizer, so a title that checks it skips the libSceDbgThreadSanitizer annotation calls, which throw. PPSA23226's bundledlibSceJobManager_nosubmission.prxmakes those calls only when it reports nonzero
Unknown function info
- sceKernelError follows shadPS4's kernel implementation: zero returns zero; other guest POSIX error values receive the
0x80020000bias without modifying errno. Arithmetic is defined modulo 32 bits to avoid signed overflow for inputs outside the errno domain. Console behavior for those inputs and PS5 equivalence have not been measured. - getlogin_r (libkernel) returns the host effective account name:
GetUserNameWas UTF-8 on Windows andgetpwuid_r(geteuid())on POSIX. A valid account returns a name even without a login session; hostgetlogin_r,USERandLOGNAMEare not consulted. The console's login name and behaviour without a session are unknown. Account lookup or encoding failures, a missing or empty account name, and POSIX account records exceeding the 16 KiB lookup buffer throw; there is no replacement name or retry. A short guest buffer returnsERANGEwithout modifying it, and a null output returnsEFAULT. - sceKernelGetSystemSwVersion (libkernel) reports baseline system software version
0x01000000("01.000.000") for ProsperoStore (PPSA99000); the console's firmware version is not modeled. Measured on PS5 firmware 13.42: record values 0, 24, 39, 40, 41, 48 and 64 in the leading 64-bit field are accepted and preserved, the 28-byte string at offset 8 and packed version at 0x24 are filled, and bytes from 0x28 onward remain untouched. This API reports "13.590.001" /0x13590001in that environment, while the Prospero variant reports "13.420.000" /0x13420000; their values must not be assumed identical. A null pointer still returnsSCE_KERNEL_ERROR_EFAULT, which was not measured; other firmware versions and record values remain unverified. - kern.rng_pseudo (libkernel) -
sysctlbynamefills the whole output buffer with bytes from the host'sstd::random_deviceand leaves the length unchanged; a write returns -1 withEPERMand a call without an output buffer throws. The only caller seen is mbedTLS'smbedtls_hardware_pollin the catalog's ProsperoLight boilerplate, which asks for 64-byte blocks and fails if fewer come back. The console's generator, its per-call maximum and its answer to a size query are unknown - sysconf (libkernel) -
_SC_CLK_TCKreturns 128, FreeBSD'sCLK_TCKas itssysconfreturns it (time.h); the console's value is not checked, and notimesreports ticks at that rate. CPython'sposixmodule stops its import with "cannot read ticks_per_second" when the value is below 1, as the Python in PS5MC (PPSA99420) does. Names other than_SC_CLK_TCK,_SC_PAGESIZE,_SC_NPROCESSORS_CONF,_SC_NPROCESSORS_ONLNand_SC_PHYS_PAGESfail withEINVAL - hw.realmem (libkernel) -
sysctl{CTL_HW, 12} andsysctlbynamereport the same 8-byte value ashw.physmem(the_SC_PHYS_PAGESpages of 16 KiB), read-only withEPERMfor a write andENOMEMfor a short buffer, as FreeBSD'ssysctl_hw_realmemdoes. FreeBSD reports there the memory the firmware describes, which is at leasthw.physmem; the console's values are not known. Dolphin reads it as the machine's memory size inMemPhysical(RetroArch PPSA99169'sdolphin_libretrocore) - vm.overcommit (libkernel) -
sysctlbynamereports the 4-byte value 0, the default FreeBSD gives itsvm.overcommitint, withENOMEMfor a short buffer; the console's value is not known and a write throws. mimalloc reads it inunix_detect_overcommitand takes 0 as no overcommit, as PS5MC (PPSA99420) does at start-up - kern.sdk_version (libkernel) -
sysctlbynamereports the system software version as the 4-byte value0x01000000(1.00), the valuesceKernelGetSystemSwVersionreports; a write returns -1 withEPERM. The major version is byte 3 and the minor version byte 2, in hex digits that read as decimal (13.60 is0x1360xxxx), as Capy Mobile (PPSA92280), remote_lua_loader and the lapse payload read it. Capy Mobile only displays it. The console's firmware version is not modeled and the low two bytes are unknown - kern.ostype, kern.osrelease, kern.version, kern.hostname, hw.machine (libkernel) -
sysctl(1.1, 1.2, 1.4, 1.10, 6.1),sysctlbynameandsysctlnametomibserve the namesunamereads as read-only strings: "FreeBSD", "11.4-RELEASE" and "FreeBSD 11.4-RELEASE\n", the values FreeBSD 11.4-RELEASE builds without source control information (newvers.sh, L293-L294), the host namegethostnamereports ("AnyPS5") and "amd64"; a short buffer returnsENOMEMand a writeEPERM. The console's strings are not measured - sceKernelGetProsperoSystemSwVersion (libkernel) reports system software version
0x01000000("01.000.000"); the console's firmware version is not modeled. The 0x28-byte block (a 64-bit size at 0, the version string at 8 and the packed version at 0x24) follows SharpProspero, which Prospero Multi Tools (PPSA99110) and Prospero Explorer (PPSA99109) are built with and which sets the size to 40, and the same fields in etaHEN and shsrv. A size other than 0x28 returnsSCE_KERNEL_ERROR_EINVALwithout writing; whether the console checks the size is unverified, since etaHEN and shsrv declare a 0x30-byte block and do not set it - sceKernelGetAllowedSdkVersionOnSystem (libkernel) reports the system software version with the 12 patch bits set,
0x01000fff, as shadPS4 does on the PS4 and as SharpProspero describes it, which Prospero Explorer (PPSA99109) reads it through; a null pointer returnsSCE_KERNEL_ERROR_EINVAL. The console's value and its errors are unverified - sceKernelConvertUtcToLocaltime, sceKernelConvertLocaltimeToUtc (libkernel) convert with the host time zone at the given instant, as libc
localtimedoes, instead of the console's time zone setting.west_secholds the standard-time offset in seconds east of UTC, despite its name, anddst_secthe daylight saving seconds in effect. This follows PPSA99203 (PS5SX2), whose author saw a console write 7200 and 3600 into the 16-byte block; which zone that console was set to is not recorded, so whetherwest_secincludes daylight saving is not settled. shadPS4 also stores seconds east. PPSA99203 declares the last argument ofsceKernelConvertUtcToLocaltimeasuint64_t*; 32 bits are written. On Windows the standard offset and the daylight saving amount are the C runtime's current_get_timezoneand_get_dstbiasfor every instant, as libclocaltime_suses them, so instants under a zone's earlier rules get today's offsets; aTZvariable does not set_dstbias, which keeps the value an earlier_tzsetread from the system (0 when the system zone has no daylight saving), and summer time then adds nothing. In a repeated or skipped local hour,sceKernelConvertLocaltimeToUtctakes the offset in effect one standard offset earlier. Its last argument receives the zone's daylight saving amount whether or not it is in effect, found by sampling the host zone over the year around the result, whiledst_secis the amount in effect: the libcmktimethat PPSA99109 bundles (text 0x70ebb-0x70ee5) subtracts that amount whentm_isdstis set outside summer time. Thatmktimepassestm_isdstas the second argument, which is ignored, so it does not choose the offset in a repeated hour.sceKernelGettimezoneand thegettimeofdaytime zone still report no offset and no daylight saving. A host time the C runtime cannot convert throws - getuid, geteuid, getgid, getegid and issetugid (libkernel) report the root identity (0) and a process that never changed credentials (
issetugid0), as a jailbroken process has; the host user and group are not exposed to the guest. The values a title sees on a console are unverified - sceKernelGetAppInfo (libkernel) - only
app_idand the title ID (offset 0x10, up to 9 characters plus a terminator) are written; callers declare the structure with different sizes, so the rest is left alone.app_idis 1, the running title's id in libSceSystemService, which is a guess, not something measured. The error codes follow shadPS4:EPERMfor another process, and 0 without writing anything for a null pointer. - AMPR object layout and compact gathers - the 24-byte object and offsets (type 0, cursor 4, count 8, capacity 12, pointer 16) follow ampr_emu assertions and its C declaration. AnyPS5 represents the 32-bit type word as low 16-bit type and high 16-bit recording flags. Command encoding is AnyPS5 internal: opcode 29 and three 32-bit words encode the high offset byte, length minus one and low offset word. The 12-byte size reduces HFW's 1 MiB buffer pressure; it is not a claim about console opcode encoding. Existing 40-bit offsets and lengths 1 through 2^32 are retained; console behavior outside those limits is unverified.
- APR resolve ForEach result (libkernel) -
sceKernelAprResolveFilepathsToIdsAndFileSizesForEachreturns the number of paths that resolved, with the per-path status inresultsand the failed ids/sizes set to the invalid id and 0. The ampr_emu bridge passes the count of its resolver through and does not show what is counted; counting successes is not confirmed on a title. The guest errno of the failing non-ForEachresolves is set through__error_nid_postfix - APR reads past the end of a file (libkernel) - a read that reaches the current end of the file on disk copies the bytes the file has and fills the rest of the destination with zeros; a read that comes back short before that end still throws. PPSA18605 resolves its settings file once, rewrites it one byte shorter and reads it again with the size that resolve returned. The console's behaviour for a read past the end of a file is unverified: it may zero-fill, leave the destination untouched or fail the command
- AMPR counter access (libSceAmpr) - signatures and checks of
WaitOnCounter_04_00,WriteCounter_04_00,ConstructNopandConstructMarkertaken from the ampr_emu reimplementation, which hands counter commands to the console and does not show their effect. The counters are taken as 32 bits each: the 8-byte access of counter n spans n (low) and n + 1 (high), the 2- and 1-byte accesses select a half or a byte of the counter by its offset, waits compare at the access width (signed and wrapped compares included), the AND mask applies to both sides and the atomic writes wrap within the access. TheConstructNoptype is not recorded - APR gather and scatter reads (libSceAmpr) - signatures, argument checks and the read cursor taken from the ampr_emu reimplementation, not confirmed on a title: each read leaves the file, the next file offset and the next destination; a gather reads that file at a new offset into the next destination, a scatter continues in the file into a new destination, a gather-scatter takes both. Recording one needs a read file since the last
ResetGatherScatterState; like ampr_emu,sceAmprCommandBufferResetkeeps that state, and a gather or scatter with no read before it in the submitted buffer throws instead of returningSCE_AMPR_ERROR_APR_INVALIDGATHERSCATTERSTATE - AMM map, map direct and unmap (libSceAmpr) - signatures and the protection mask (CPU, GPU, AMPR and ACP read and write) taken from the ampr_emu reimplementation, which hands the remaining checks and the mapping to the console kernel; the mapper model taken from PS5PCEM:
sceAmprAmmGiveDirectMemoryusage 1 gives the allocated direct memory to the pool plain maps take pages from, usage 0 only allocates it. Not confirmed on a title. The AMM virtual address ranges are a 256 GiB range (PPSA02015 places its heaps 64 GiB above the start of the range, so 32 GiB is too small, and reached 84 GiB above it in its first 159 s; KytyPS5 reports 512 GiB, but every 64 GiB-aligned 544 GiB window in the guest arena covers 448 to 608 GiB, and on Windows one host thread stack there is enough to block it) and a 32 GiB multimap range reserved on first use at the top of the guest arena with the start on a 64 GiB boundary (PPSA02015 tells its pool pointers from its heap pointers by address bits 28 to 35, which needs such a start for its layout), above the addresses that maps withoutMAP_FIXEDare given lowest first and that titles derive their next hints from; the console's addresses are not known (ampr_emu takes them fromsceKernelMapperGetParam), and a reservation that cannot be made there throws. Maps outside them throw. Addresses, sizes and direct offsets must be 16 KiB aligned. The memory type and the GPU mask are ignored, AMPR and ACP access maps as CPU access, unmapped pages go back to the pool and stay reserved. Submission runs the buffer before returning, the submit result is always 0 and a failing command throws; the AMM measures return errors sign-extended, as ampr_emu does. A map returns its target to the reservation before releasing the old pages, so pool exhaustion leaves that target reserved; mapping failure also restores the reservation before returning newly taken pages to the pool. The console's map failure and rollback behavior is unverified.sceAmprAmmGetVirtualAddressRangesthrows for a null output - AMM remap, multimap and protection changes (libSceAmpr) - signatures and the protection and mask checks taken from ampr_emu; the effects are inferred from the names, not confirmed on a title. A remap moves every page of a fully mapped range to the new address with the given protection and leaves the old range reserved; a multimap maps the same pages at a second address, anywhere in the AMM ranges, and a pool page goes back to the pool once its last mapping is unmapped; a multimap onto its own source range throws. A protection change sets the masked bits on every page of a fully mapped range; the memory type is recorded and ignored. A command on a range that is not fully mapped throws
- AMM PRT ranges (libSceAmpr) - signatures, checks and their order taken from ampr_emu, where
MapAsPrtis a map with the PRT flag and protection 0 andAllocatePaForPrta memory type and protection change with mask 1019, both checking the buffer memory after its size; the effects are inferred from the names, not confirmed on a title. Unbacked PRT pages are zero-filled pages with GPU read access, so CPU reads see zeros instead of faulting and GPU writes fault instead of being dropped.AllocatePaForPrtbacks each unbacked page with a pool page and sets the type and protection of backed ones;RemapIntoPrtmoves a fully mapped range into a PRT range, its opcode argument (1011 when 0, as in ampr_emu) is recorded and ignored;UnmapToPrtreturns the pages to the pool and zero-fills them again. A plain map, unmap or remap over a PRT range ends it;AllocatePaForPrt,RemapIntoPrtorUnmapToPrtoutside a PRT range throws - AMM kernel write commands (libkernel) - signatures of
sceKernelWriteModifyMtypeProtectCommandandsceKernelWriteRemapIntoPrtCommandtaken from ampr_emu's libkernel hook targets, which forward to the real kernel; the measure mode (dstnull reports the size,outSizenull skips the report) follows ampr_emu's measure-then-record calls, not confirmed on a title. The range, protection and mask checks mirror thelibSceAmprsiblings, and the recorded bytes match them exactly so the submitted buffer executes on the existing paths - sceCoredumpWriteUserString (libkernel, NID
32KQRUK13kI) -(uint32_t, const char* text, int)follows the console'slibkernel.sprx(SDK 4.5x build ofJ03950001) at 0x13050: a nulltextreturns0x81180000; otherwise the three arguments go to syscall 601 (command 10), whoseEPERMbecomesSCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER(0x81180003),EINVAL0x81180000, and a success returns the value the kernel wrote back. Measured on a PS5 (firmware 13.60) from a homebrew payload with no core dump handler: the call succeeds with 0, the kernel rejects a first argument above 15 or a third outside 0 to 256 with0x81180000, and a 2047-character text is accepted. Those checks are reproduced and anything else returns 0; nothing is recorded, as there is no dump, and what the two numbers mean is unknown. PPSA16064 imports it - sceCoredumpConfigDumpMode (libkernel, NID
gzLt9Qrauk0) -(int mode)follows the samelibkernel.sprxat 0x134D0: a mode other than 1 returns0x81180000; mode 1 issues syscall 601 (command 16, arguments 0 and 1) and returns 0 whether or not it fails, since only a raw return of 22 would map to0x81180000. No dump is produced here, so mode 1 returns 0 without the syscall. PPSA16833 imports it - sceCoredumpGetStopInfoGpu_Agc (libkernel, NID
Jrs7UUkGOFo) -(void* info, size_t size)follows the samelibkernel.sprxat 0x137E0: a nullinforeturns0x81180000; syscall 601 (command 21) failing withEPERMreturnsNOT_IN_COREDUMP_HANDLER; otherwise twolibSceAgcDriverimports (NIDs+NIAY3DWpOwandLgctzJSGy7s, names unknown) decide between0x81180005and, for asizeof 8, storing the second one's 64-bit result and returning 0, with any other size returning0x81180000. The handler is never invoked here, so a non-nullinforeturnsNOT_IN_COREDUMP_HANDLERwithout writing, assumingEPERMoutside the handler as above; the AGC branch is not reached - sceKernelInternalMemoryGetModuleSegmentInfo (libkernel) - the
(InternalSegmentInfo* result)signature, the 16-byte{address, size}result and the choice of the main program's first segment follow fpPS4 only, which fills it through itssceKernelGetLibkernelTextLocationfrom the first segment ofeboot.bin; whether the console reports that segment, the text of libkernel itself or the calling module's is unknown. The segment is the first onesceKernelGetModuleInforeports for the main program, and 0 is returned. A null result throws. 44 audited titles that bundle their ownlibc.prximport it; no call has been observed - sceKernelGetModuleInfoForUnwind (libkernel) - on Linux the tables come from
sceKernelGetModuleInfoFromAddr, whoseeh_frame_sizeincludes the zero terminator, as measured on firmware 13.60 (reported in #2419, not reproduced here); on Windows it is assumed to stop before the terminator. LLVM libunwind'sCFI_Parser::findFDEstops at the terminator or the length, whichever comes first, so it finds the same FDEs either way. Only the main executable and.guest.prximages report tables; other host images report none, as on Windows - sceKernelGetModuleInfoForUnwind (libkernel) - on Windows the
.ehmetatables and the image file name are cached per allocation base and dropped after a loader free (dlclose,sceKernelStopUnloadModule, the thread atexit release) when the module is no longer loaded. A table changed in place, or an image freed by anything other than the loader, is not noticed. An address inside an image of the Windows loader's module list whose tables are cached is resolved withRtlPcToFileHeaderand noVirtualQuery; other addresses, and images without tables, still useVirtualQuery. The.eh_framewalk that finds its end is done once per image instead of once per call - sceKernelGetModuleInfoFromAddr (libkernel) - the 0x1A8-byte info layout follows the shadPS4 reimplementation; a title's
libc.prxreads onlyid(PPSA14632). The meaning offlagsis unknown; only 2 is accepted. The inputst_sizeis ignored, because PPSA21703'slibc.prxpasses an uninitialized 0x1A8-byte buffer; the full size is written back. The id is thesceKernelLoadStartModulehandle of an image opened through the loader and otherwise a stable id from the same range. The name is the host file name without.guest.prx. An image with more than 4 load segments (the converted executable has 7) reports its first 4.tls_offsetis 0 andref_countis 1, as the host loader exposes neither. On Windows the module is the PE image holding the address: a relinked image reports its.elfsections as segments and the eh_frame_hdr recorded in.ehmeta, any other image its first 4 sections with memory access and the.ehframsection without an eh_frame_hdr; the TLS fields andinit_proc_addr/fini_proc_addrare 0 - sceKernelGetModuleList (libkernel) - lists the main program and every loaded
.prximage in load order; host runtime libraries are left out. The handles are the idssceKernelGetModuleInfoFromAddrreports, not the console's. The(int32_t* handles, size_t count, size_t* actual)signature and returningENOMEMwithout writingactualafter filling the firstcounthandles when more images are loaded follow shadPS4; whether the console writes any handles in that case is unknown - sceKernelGetModuleInfo (libkernel) - the 0x160-byte layout (
st_size,name[256], 4 segments,segment_count,fingerprint[20]) follows fpPS4 and shadPS4. Only thatst_sizeis accepted, as in shadPS4; other sizes returnEINVAL, a null pointerEFAULTand a handlesceKernelGetModuleListdoes not listESRCH. Name and segments are the onessceKernelGetModuleInfoFromAddrreports, on Windows from the same PE sections, soprotuses the read/write/execute bits 1/2/4, while fpPS4 and shadPS4 store the ELFp_flags. The fingerprint is left zero, as the host image does not carry it - sceKernelGetModuleList2 and sceKernelGetModuleInfo2 (libkernel) - the title-module variants follow shadPS4:
sceKernelGetModuleList2lists whatsceKernelGetModuleListlists without system libraries, andsceKernelGetModuleInfo2answersEPERMfor a system library after theEFAULT,EINVALandESRCHchecks ofsceKernelGetModuleInfo. A system library here is a loaded.prximage that is not a relinked title module (.guest.prx); the main program and the title's own modules are listed. Not verified on the console. PPSA34063 imports both and fails to load without them - sceKernelGetLibkernelTextLocation (libkernel) - the
(void** address, std::size_t* size)signature and the choice of the main program's first segment follow fpPS4; whether the console reports that segment, the text of libkernel itself or the calling module's is unverified. The segment is the first onesceKernelGetModuleInforeports for the main program, and 0 is returned. A null argument throws. - sceKernelReleaseFlexibleMemory (libkernel) - unmaps the range like sceKernelMunmap, as fpPS4 and prosper do; whether the console keeps the virtual range reserved after releasing its pages is unverified
- sceKernelAvailableFlexibleMemorySize (libkernel) - the segments of the title's executable and of its
.guest.prxmodules count against the configured flexible-memory budget (448 MiB by default) while loaded, for this size and for flexible maps. A title may declare a larger budget throughkernel.flexibleMemorySizeinparam.json; this does not reduce the 13.5 GiB direct-memory size. Rejection of zero or a value not aligned to 16 KiB has not been measured on a console. This segment-accounting model follows shadPS4, which maps every segment of a module outside its system module directory as flexible memory on the PS4. Counted are the segments the title's ELF gives permissions, each rounded to 16 KiB pages; not counted are segments without permissions, the relinker's own blocks (on Linux both are the read/write/executePT_LOADs, plus the header block at file offset 0; on Windows everything outside the.elfsections) and the host system libraries. The Linux image carries no marker for the relinker's blocks, so they are told apart by those flags and that offset only: a title segment that is writable and executable, or starts at file offset 0, is not counted. A module whose segments exceed what is left still loads, and the available size then reads 0; shadPS4 fails the segment map instead (memory.cpp returnsEINVAL, module.cpp asserts). Which segments the PS5 charges, its rounding, whether system modules use the title's budget and when an unload releases the charge are not measured. PPSA15508 subtracts the reported size and a 10 MiB module allowance from its own 448 MiB budget at start-up and stops when the result exceeds its 146 MiB code and data limit (#2883) - Memory protection bits 0x100 and 0x200 (libkernel) - taken as audio coprocessor (ACP) read and write, as in the KytyPS5 and etaHEN headers; the coprocessor work runs on the host CPU, so they grant host read and read/write access like the GPU bits
- Protecting a host thread's stack (libkernel) - on Windows
sceKernelIsStackreports a host thread's whole stack reservation, which Windows commits on demand from the top down behind a guard page. A protect over that range changes only the pages above the guard; the guard page and the reserved pages below it are left as they are, so the stack still grows and an overflow still raises a stack overflow. A protect that starts at the bottom of the reservation and stops short of the guard page, as HotSpot makes its stack guard zones, is committed and protected. Windows moves the guard page down by committing the pages below it as read/write guard pages, whether they were reserved or committed, so those pages come in read/write as the stack grows to within one guard region of them (three pages on GitHub's runners). A protection other than read/write on them,PROT_NONEincluded, does not hold once the stack is that close, where on the console every page of the reported range takes the protection. Holding it would mean removing the thread's guard page and keeping the stack limit in its TEB by hand. On Linux the reported range of the main thread is mostly unmapped; the unmapped pages from the bottom of a protect up to the top of the stack are mapped read/write in place, then the protect is applied, so the stack never has to grow down to the protected pages, which Linux refuses within its stack guard gap (1 MiB by default) above a readable mapping. The pages below a protect stay unmapped and out of the stack's reach, where on the console the stack goes on below them - Foreign guest module image protection on Windows (libkernel) -
sceKernelMprotecton Windows allows protection mutations on mapped PE images containing a.elf0section (as relinked guest modules produce, such as NVIDIA Real-time DenoiserslibNRD.prxbundled in Dying Light 2: Stay Human PPSA02262), registering their sections intoGuestAllocations. Host libraries and system DLLs (such aslibkernel.prxandkernel32.dll) without a.elf0section continue to be rejected - _umtx_op (libkernel) -
UMTX_OP_WAIT,UMTX_OP_WAKE,UMTX_OP_WAIT_UINT,UMTX_OP_WAIT_UINT_PRIVATE,UMTX_OP_WAKE_PRIVATEandUMTX_OP_NWAKE_PRIVATEfollow FreeBSD 11's kern_umtx.c: a timeout whose size argument is at most 16 is a relativetimespec, a larger one an_umtx_time, and a wake of 0 or fewer wakes one waiter.UMTX_OP_NWAKE_PRIVATEreads the value as anintcount of pointers at the object and wakes every waiter on each one; a count of 0 or fewer reads nothing, and a null list fails withEFAULT, where FreeBSD'scopyinwould also fail for any other unreadable list. Waiters share the queue ofsceKernelSyncOnAddressWait. Operation 0 fails withEOPNOTSUPPand operations above 26 withEINVAL, as in FreeBSD 11;UMTX_OP_WAIT_UINTwith a value that does not fit in 32 bits returns 0 at once, as FreeBSD's zero-extended compare does. Operation 1 throws, since PPSA12544's call_once uses it in a way FreeBSD rejects; the other operations throw, and so does a misaligned object. PPSA12544'slibc.prxand Unity'sPS5Util.prx(PPSA18194) passUMTX_OP_WAITandUMTX_OP_WAIT_UINTa pointer to a 4-byte microsecond timeout with size argument 0, which FreeBSD reads as atimespec, and wait for a running call_once with operation 1. Whether the console reads a microsecond timeout there in all cases, and what operation 1 does on it, is unverified. A call with size argument 0 is read unconditionally as a 4-byte microsecond timeout, matching callers likelibc.prxandPS5Util.prx'szlqfTyrQSPkwrapper which pass size 0 with a 32-bit microsecond count - sceKernelSyncOnAddressWait (libkernel) - the only known caller passes a null timeout and a name string as the fourth argument; the timeout is assumed to point to microseconds like the other kernel waits, and the name is ignored
- sceKernelSyncOnAddressWait8 / sceKernelSyncOnAddressWait16 / sceKernelSyncOnAddressWait32 / sceKernelSyncOnAddressWait64 (libkernel) - libc's
__std_atomic_wait_direct_8/16/32/64pass 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 - scePthreadRwlockDestroy, pthread_rwlock_destroy (libkernel) - measured on a PS5 (firmware 13.60): destroying a rwlock returns 0 whether it is free or held for reading or writing, and leaves 1 in the handle; every call on that handle then returns
EINVALuntil it is initialized again, and a zero handle is initialized on first use by thescePthreadRwlockcalls as by the POSIX ones. A rwlock destroyed while held is not freed, so its memory leaks. Destroying a rwlock while another thread is acquiring it is not guarded - scePthreadCondWait / scePthreadCondTimedwait (libkernel) - before waiting, FreeBSD libthr's
cond_wait_commonchecks ownership through_mutex_owned: an unowned mutex returnsEPERM, while its destroyed marker returnsEINVAL. An untouched adaptive static initializer (slot value 1) therefore returnsEPERM; the test covers this, an uninitialized slot, a mutex owned by another thread, and the destroyed marker for both timed and untimed sce and POSIX waits. A recursive mutex locked several times is released completely for the wait and relocked to the same depth afterwards, as FreeBSD libthr does (_mutex_cv_unlock,_mutex_cv_lock). These FreeBSD-derived behaviors remain unverified on console hardware. Cancellation follows the POSIX condition wait contract: the original mutex and recursive depth are restored before cleanup, and a canceled waiter forwards a wake to the remaining waiters. The host condition state is retained across mutex reacquisition so the guest can destroy and free its condition after notification; cancellation ordering and signal replacement are not measured on a console - sceKernelAioSubmitReadCommandsMultiple / sceKernelAioSubmitWriteCommandsMultiple / sceKernelAioWaitRequests / sceKernelAioPollRequests / sceKernelAioCancelRequest / sceKernelAioCancelRequests / sceKernelAioDeleteRequests (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, poll copies every state without waiting); 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
- scePthreadAttr setters and getters (libkernel) - measured on a PS5 (firmware 13.60): a null or uninitialized attribute returns
EINVAL, as does a null output of the stack size, detach state and scheduling parameter getters; a policy other than 1 and 3, an inheritance other than 0 and 4, or a priority outside 256 to 767 (or a null parameter) returnsENOTSUP(0x8002002D); a detach state other than 0 and 1 and a stack size below 16384 returnEINVAL; any affinity mask is accepted. A null attribute pointer toscePthreadAttrInitand a null output ofscePthreadAttrGetaffinitycrashed the payload, so they still throw. A new attribute holds a 64 KiB stack size on the console and 1 MiB here, which the host code that runs on guest threads needs - sceKernelGetAvailableCpumask (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 (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 FreeBSDcpusetguarantees on the console. Which CPU the console reports for a thread allowed on several is not known; shadPS4 returns 0 - pthread_attr_setsolosched_np (libkernel) takes
(attr, int)and stores the value throughscePthreadAttrSetsolosched, returningEINVAL(22) for a null or uninitialized attribute like the other POSIX attribute setters. Only the name is public (aerolib2+pVfgiEd7A); the signature and behaviour follow the SCE twin, which KytyPS5 and sharpemu store as anintwithout a range check, and PS5PCEM implements this alias over it. On a PS5 (firmware 13.60) only 0 and 16 are accepted; every other value returnsENOTSUP(45,0x8002002Dfrom the SCE call) and keeps the stored one, and the SCE calls returnEINVALfor a null or uninitialized attribute. PPSA15878 passes 16. Threads created from the attribute are not scheduled differently; what solo scheduling does on the console is unknown. Returnal (PPSA01285 v03.000.005) stopped on it withwhat(): pthread_attr_setsolosched_np_nid_postfix not implemented(#2872, #1155); LEGO Horizon Adventures (PPSA14632, #734) and Palworld (PPSA23126, #2598) import it - sceKernelMapFlexibleMemory / sceKernelMapNamedFlexibleMemoryInternal (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 an SDK 9libc.prxheap, which passes it with read/write protection and uses the mapping as an olderlibc.prxusessceKernelMapNamedFlexibleMemorywith flags 0: through the Internal variant in Astro Bot PPSA21564, throughsceKernelMapFlexibleMemoryin Animal Well PPSA22520 01.000.011 - sceKernelMtypeprotect (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.
sceKernelBatchMap2operation 4 takes the same path with the entry type - Map hints inside a reservation (libkernel) -
sceKernelMapDirectMemoryand the flexible and anonymous maps place a mapping withoutMAP_FIXEDat its hint address when the whole range lies in a range still reserved bysceKernelReserveVirtualRange, as ifMAP_FIXEDwere set. Taken from PPSA01474, which reserves 64 GiB and then maps its heaps inside it with hints and noMAP_FIXED, and uses the hint addresses afterwards; not measured on the console. Hints outside a reservation, or over a range that is partly mapped, stay hints - Fixed anonymous mmap (libkernel) -
MAP_PRIVATE | MAP_ANON | MAP_FIXEDreplaces the requested range through the existing guest mapping machinery and zeroes the new memory.MAP_EXCLis checked under the allocation-registry mutation lock, including virtual reservations, and returnsENOMEMwithout changing an occupied range; withoutMAP_FIXEDit isEINVAL. Lengths are rounded to 16 KiB, while fixed addresses must be aligned and the complete rounded range must lie inside the guest arena. This follows FreeBSD 11.4 argument validation and rounding, fixed replacement, and collision errno. The console behaviour has not been measured. AnyPS5 starts the guest arena at 0x200000000, so the fixed path rejects the low 0x400000 address requested by PICO-8 PPSA99808. The console address-space layout is unmeasured; executable placement outside the host arena is tracked in #5217. Static catalog disassembly finds0x1012in PPSA98001/PPSA99808 and0x5012in PPSA99808; these titles were not run. - Mapping hints with no room above them (libkernel) - a mapping without
MAP_FIXEDwhose hint lies above the guest address space, or has no free range at or above it, is placed from the bottom of the guest arena, as FreeBSD 11.4'svm_mmap_objectretries throughvm_map_find_minfrom its minimum address. FreeBSD's minimum is the end of the largest possible data segment rather than the start of the arena, and the console's address-space layout is not measured. PS5CEMU-HAR (PPSA99360) givessceKernelReserveVirtualRangea hint near its own code, which is a host address here because the relinked executable lies outside the guest arena, so its JIT gets memory more than 2 GiB from its code and does not start - _sceKernelRtldThreadAtexitIncrement, _sceKernelRtldThreadAtexitDecrement (libkernel) - the argument is the
dsoaddress a title'slibc.prxpasses to__cxa_thread_atexit(PPSA14632), which fails when Increment returns nonzero. As in the fpPS4 reimplementation, Increment takes a reference on the loaded image containing the address and Decrement releases one; the variants without the leading underscore are assumed to match - tcgetattr, tcsetattr (libkernel) - every file and pipe descriptor is taken as not a terminal (
ENOTTY), and a socket getsEADDRNOTAVAILforAF_INETandEOPNOTSUPPotherwise, as FreeBSD'sin_controlandpru_control_notsuppanswer a termios ioctl; what descriptors 0 to 2 of a title are on the console, and whether any of them is a terminal there, is unknown - select (libkernel) - descriptor 0 is never reported readable and descriptors 1 and 2 are always writable, as for a console title with no input; reads from descriptor 0 still go to the host stdin.
/dev/randomand/dev/urandomfollowrandomdev_pollfor a seeded device underselscan, which asks once with the flags of every set the descriptor is in: in the read set they are readable and reported in no other set, and otherwise they are reported in the write and exception sets they were asked in. A regular file, a directory or a file read from app0.zar is always readable and writable and never in the exception set, asvop_stdpollanswers, on every host. Guest sockets and libSceNet sockets are polled. A kqueue is readable while one of its events is triggered and is never writable or in the exception set, as FreeBSD 11.4'skqueue_pollanswers, on every host. Other descriptors are polled through the host on Linux, while on Windows pipes are checked every millisecond with the readiness testpolluses and any other descriptor throws. Dungeons (PPSA16064) selects on descriptor 0, and RetroArch (PPSA99169) selects on its web server's wake-up pipe next to its listening socket - select (libkernel) - a descriptor the socket poller reports as unknown that is below
GuestSockets::FirstDescriptoris polled on the host instead of throwing (on Windows only a pipe, as above); an invalid host descriptor fails withEBADF, and mixed host and socket sets are polled in short alternating slices so neither side waits out the full slice alone. A descriptor that stops being a socket, a kqueue or a pipe during the wait fails the call withEBADFwhen it is closed and is selected again with the rest of the timeout when its number was reused, as FreeBSD 11.4'skern_selectlooks every descriptor up again in each pass. A close that has already removed the descriptor from the guest's kqueue, pipe or file tables but not yet closed its host descriptor is seen by that new look-up as a descriptor of unknown kind, which throws on Windows and is polled through the host on Linux. Wobbly Life (PPSA29413) selects on file descriptors while streaming its bundles - __progname (libkernel) - unknown data export
- environ (libkernel) - data export pointing to a fixed empty environment so titles that import it load; it is not the environment libc's
getenvreads, so it lists none of those variables andsetenv/putenvchanges do not appear in it - seteuid, setegid, setuid, setreuid, setregid, setgroups, getgroups (libkernel) - the guest process is assumed to run as root, with real, effective and saved user and group ids 0. As in FreeBSD's
sys_seteuid,sys_setegid,sys_setuid,sys_setreuidandsys_setregid, id 0 (or -1 for an unchangedsetreuid/setregidid) is then accepted without a privilege check and changes no credential, so 0 is returned; any other id would change an id, which is not modelled, and throws.setgroupskeeps the supplementary group list askern_setgroupsdoes (NGROUPS_MAX1023, an empty list leaves the effective group as the only entry, a first entry other than 0 would change the effective group and throws) andgetgroupsreports it. The ids and privileges a title has on a console are unverified - pthread_getname_np (libkernel) - a thread that has already been joined returns
ESRCH(3) and its output buffer is left untouched, as measured on a PS5 (firmware 13.60) by the author of this entry; since #4481 the joined thread's object is retained, so the posix wrapper answersESRCHfor it instead of reading the retained name. A null name buffer is answered withEFAULTbefore that, an order the console table does not measure. The SCE siblingscePthreadGetnamehas no measured joined-thread behaviour on the console and still reads the retained object's name, so it is unchanged, as is the pre-existing dangling case of a thread that ended detached (its object is released at exit; see the scePthreadJoin entry) - sceKernelGetOperationMode (libkernel) - the signature
(int* mode, int* submode)comes from PPSA12544, which logs both values after the call and ignores the result. A homebrew payload on a PS5 (firmware 13.60) reads mode 2 and submode 0, nothing else is written, and the result register holds no status, so the function is taken to return nothing. Whether a title process reads the same values is unverified. Null outputs throw - sceCoredumpGetStopInfoCpu (libkernel) takes
(void* info, size_t size)as in the Orbital type database; the 16-byte, 8-byte-aligned block follows SharpProspero, which places coredump reads inside the handler. The handler is never invoked here, so a well-formed request returnsNOT_IN_COREDUMP_HANDLERwithout reading or writing the block. Its value0x81180003follows shadPS4 and the NHL 27 (PPSA34063) call site reported in #4275, not a PS5 measurement. The console validation order is unverified: null, unaligned or wrapping buffers and sizes other than 16 throw instead of guessing an argument error. Information from an active coredump handler remains unsupported - sceCoredumpGetThreadContextInfo (libkernel, NID
ShChva57wIM) takes(void* threadContextInfo, size_t threadContextInfoSize)as in the Orbital type database; the 144-byte block of 18 64-bit registers follows SharpProspero. It answers assceCoredumpGetStopInfoCpu: the handler is never invoked here, so an aligned 144-byte request returnsNOT_IN_COREDUMP_HANDLERwithout reading or writing the block, and null, unaligned or wrapping buffers and other sizes throw, as the console validation order is unverified. Planet Coaster (PPSA01736) imports it - sceCoredumpAttachMemoryRegion, sceCoredumpAttachUserMemoryFile (libkernel) - both take
(uint32_t userValue, const void* mem, size_t size): for the first, the IDA type database of Orbital and the SharpProspero bindings agree; the second has only SharpProspero (the PS4 5.05 firmware does not export it, so Orbital has no entry) and is described there as attaching the region as an unnamed user file. Not confirmed on a title; the RED engine's PS4 crash handler (Vangaurd) calls the siblingsceCoredumpAttachMemoryRegionAsUserFile(999, buffer, bytes, name)from inside its handler. Attaching belongs inside the core dump handler, which the system runs on its own thread after the process stops (SharpProspero's remark, the RED engine's usage andSCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER, "function was not called from registered core dump handler", in the Vita's SceCoredump error table, RivetDB). The handler is never invoked here, so every call is outside it and returns that error; nothing is recorded, as there is no dump to attach to. Its value0x81180003comes from shadPS4 (PS4), not confirmed on a PS5: fpPS4 hasNOT_REGISTERED0x81180001 andALREADY_REGISTERED0x81180002, shadPS4'scoredump_error.hhasNOT_IN_COREDUMP_HANDLER= 0x81180003, and the Vita table numbersPARAM0,NOT_REGISTERED1,ALREADY_REGISTERED2 andNOT_IN_COREDUMP_HANDLER3. Whether the console checks the region before the calling context is unknown, so a null, empty or wrapping region throws.userValueis not interpreted; SharpProspero names its bits 31 and 30 as truncation flags for the user file variants - sceCoredumpWriteUserData, sceCoredumpDebugTextOut (libkernel) - the signatures
ssize_t (const void* data, size_t size)andvoid (const char* str, int len)follow the Orbital IDA type database and the SharpProspero bindings ("Repeated calls append", "The bytes written, or a negative error code"). User data is written from inside the core dump handler: the Vita'ssceCoredumpWriteUserDatareturns0x800A0003("No user-data capture is active") outside it, and the RED engine's PS4 crash handler (Vangaurd) makes all its coredump calls,sceCoredumpDebugTextOutincluded, from its handler. The handler is never invoked here, sosceCoredumpWriteUserDatareturnsSCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER(0x81180003 in shadPS4'scoredump_error.h, PS4 numbering) sign-extended to 64 bits and keeps nothing; whether the console checks the buffer before the calling context is unknown, so a null, empty or wrapping buffer throws.sceCoredumpDebugTextOutreturns nothing; its text, which goes into the report on the console, is printed to stderr with the[coredump]prefix ofsceKernelDebugWriteCppExceptionInfo, and a negative length or a null text with a nonzero length throws. No title is seen calling either: 007 First Light (PPSA11386) importssceCoredumpWriteUserDataand does not call it before its stop (#2523) - sceCoredumpAttachUserFile, sceCoredumpAttachMemoryRegionAsUserFile (libkernel) - the signatures
int (uint32_t userValue, const char* path)andint (uint32_t userValue, const void* mem, size_t size, const char* name)follow the Orbital IDA type database and the SharpProspero bindings. The crash handler calls both from inside its core dump handler,sceCoredumpAttachUserFile(priority, absolutePath)andsceCoredumpAttachMemoryRegionAsUserFile(999, buffer, bytes, "LoggerQueueCrashDump.txt"). The handler is never invoked here, so both returnSCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER(0x81180003, PS4 numbering from shadPS4) and attach nothing; whether the console checks the arguments before the calling context is unknown, so a null path or name and a null, empty or wrapping region throw. An empty path or name is passed through to that error, anduserValueis not interpreted. No title is seen calling either - sceKernelAddTimerEvent (libkernel) -
usecis taken as the period in microseconds; re-adding an existing timer keeps its pending expiration count and restarts the period from the time of the call, where FreeBSD 12+filt_timertouchclears pending expirations onEV_ADD;usec == 0fires on every wait. None of this is confirmed on a PS5 title - sceKernelWaitEqueue (libkernel) - a level-triggered user event (
sceKernelAddUserEvent) is reported at most once per call, as in FreeBSDkqueue_scan(sys/kern/kern_event.c), which re-queues a reported knote withoutEV_CLEARorEV_DISPATCHbehind the scan marker, and kevent(2), where several triggers do not produce several kevents. Not measured on a console. Events are scanned in list order and the list is not rotated, so whennumis smaller than the number of ready level events the later ones can starve, where FreeBSD moves a reported level knote to the tail. Several completions of one AMPR or VideoOut event within one call are returned as separate entries, where FreeBSD would aggregate them; the Orbis behaviour is unconfirmed - AC1FtjqMCL0 (libkernel) - unknown name, signature
- 0iEKNAvT600 (libkernel) - unknown name, signature
- 2cELEBPdYQ0 (libkernel) - unknown name, signature
- 5xYxiOtOccA (libkernel) - unknown name, signature
- 6W+shIH315Q (libkernel) - unknown name, signature
- 7ilObq815O4 (libkernel) - unknown name, signature
- 7iq7Hfs8HBk (libkernel) - unknown name, signature
- AMJZhpC+siY (libkernel) - unknown name, signature
- E9XkHowt+dY (libkernel) - unknown name, signature
- GADjreszOgM (libkernel) - unknown name, signature
- Giyg2xGrXDU (libkernel) - unknown name, signature
- KC3y21wrqzc (libkernel) - unknown name, signature
- Qb8wS4GZUsw (libkernel) - unknown name, signature
- aAQZSjb2fho (libkernel) - unknown name, signature
- pnz21I6VjDs (libkernel) - unknown name, signature
- qk043QPsrZU (libkernel) - unknown name, signature
- uDGXwUKqi8s (libkernel) - unknown name, signature
- sceKernelGetCpuTemperature, sceKernelGetSocSensorTemperature (libkernel) - read the host's thermal sensors: on Linux from
/sys/class/hwmonand/sys/class/thermal, on Windows from the ACPI thermal zones (MSAcpi_ThermalZoneTemperatureover WMI) and the storage temperature property of the first physical drive. Sensor 0 is the CPU/package value, 1 the first other value and 2 an NVMe value; a host that exposes none reports every sensor as unsupported (EINVAL). The console's SoC channel count and meanings are hardware-dependent and differ from the host's, the host sensors are not the console's, the ACPI reading is unavailable on many machines, and the storage temperature property is numbered 52 on Windows 10 2004 and later and 22 before it, so the mapping, the values and the unsupported-input error are unverified; the storage channel was checked against the drive's own SMART composite temperature on one NVMe host (StorageDeviceTemperatureProperty, 36 °C on a WD_BLACK SN7100).sceKernelGetCpuTemperaturereports sensor 0. PPSA99058 and PPSA99420 call both - LinuxProtFromSce (libkernel) - protection bits 0x100 and 0x200 are accepted and assumed not to affect host CPU access; host protection is derived only from the read, write and execute bits
- inet_pton, inet_ntop (libkernel) - the text forms are assumed to be those of FreeBSD 11.4 and are unverified on a console:
inet_ptonaccepts only four decimal parts without leading zeros forAF_INETand the RFC 4291 forms forAF_INET6(one::standing for at least one group, a dotted-quad tail only as the last 32 bits, no zone id), andinet_ntopprints lowercase hex with the first longest run of two or more zero groups compressed, and a dotted tail only for::a.b.c.dand::ffff:a.b.c.d. They no longer depend on the host resolver library - sceKernelIsThreadSanitizerEnabled (libkernel, NID
LzoM-wVLJDE) - no source gives the signature;int (void)is assumed fromsceKernelIsAddressSanitizerEnabledand from PPSA23226's bundledlibSceJobManager_nosubmission.prx, which passes no arguments and tests only the 32-bit result - getdtablesize (libkernel) - returns the smaller of the
RLIMIT_NOFILEsoft limit ofgetrlimitand the host'sgetdtablesize(), capped atINT_MAX: FreeBSD returns the smaller of theRLIMIT_NOFILEsoft limit andmaxfilesperproc(sys_getdtablesize), and the host call supplies that bound on macOS, where the soft limit alone is not one (setrlimitaccepts soft limits abovemaxfilesperproc,RLIM_INFINITYincluded);maxfilesperprocis not measured on the console. The guest limit is the host's soft limit, raised to the hard limit when libkernel loads only on Linux:core/libs/prx/libkernel/File/src/OpenFileLimit.cppis__linux__-only, so on macOS the guest gets whatever soft limit the host process inherited. VLC (PPSA85300) imports it for aclosefromloop in the child of its spawn helper (libc_stubs.c), which never runs because itsforkfails withENOSYS - close, sceKernelClose (libkernel) - descriptors 0 to 2 that are open when libkernel loads belong to the host process: closing one fails with
EBADFand leaves it open. The console's result is unknown. PPSA21703 callssceKernelClose(0)repeatedly; closing the host stream let the host C runtime give descriptor 0 to the next file opened, which the next call then closed
Functional
- Guest thread scheduling (libkernel) - the policies and priorities that
sched_get_priority_max,sched_get_priority_min,pthread_setschedparamandscePthreadSetschedparamaccept, and those a created thread takes, follow a PS5 (firmware 13.60), but they are only recorded and reported: they are not applied to host threads, so a title's priority order has no effect on host scheduling - sceKernelMprotect, sceKernelMtypeprotect (libkernel) over a range with a gap that is not registered in the allocation tracker protect the registered parts and skip the gap, and return 0, as FreeBSD 11.4's
vm_map_protectdoes (it has no check for holes); a range whose start is unmapped (also when later pages are mapped) returnsSCE_KERNEL_ERROR_EINVAL(mprotect:EINVAL) because an existing test pins that for an unmapped start; FreeBSD answers 0 there too (vm_map_protecttakes the next entry), so this errno is not taken from the source and was not changed.sceKernelMprotectthrew for a gap before, which ended the title. Every part is checked for a GPU or image pin before the first one is protected; the parts are then protected one after the other, so a host protection failure in a later part leaves the earlier parts changed (FreeBSD'svm_map_protectcannot fail in its second pass). The behaviour on the console is not known. None of this was measured on a console. - sceKernelMprotect protection bits (libkernel) - bits outside
0x3F7are dropped instead of refused, as FreeBSD 11'ssys_mprotectdoes withVM_PROT_ALL. Onimusha: Way of the Sword (PPSA27836 01.000.001) maps direct memory with0xf2and then protects it with0xfffffff2. Not measured on a console; the map calls andsceKernelMtypeprotectstill refuse undefined bits - JIT shared memory (libkernel) -
sceKernelJitCreateSharedMemory,sceKernelJitCreateAliasOfSharedMemoryandsceKernelJitMapSharedMemorytake the signatures of Orbital'stools/ida/db_types.json(name, length, maximum protection, descriptor out; descriptor, maximum protection, descriptor out; descriptor, protection, address in and out), which ps4sdk, ps5-jar-loader and the call sites in RetroArch PPSA99169, XPSemu PPSA97358 and VLC PPSA85300 match; SharpProspero declares the alias as returning the descriptor. The object is a host anonymous file shared by its descriptors (amemfdon Linux, a delete-on-close temporary file mapped as an executable section on Windows), the alias is a duplicate descriptor, and every map maps the whole object, so writes through one view show through the others; a non-null address is a hint, as inmmapwithoutMAP_FIXED. Not measured on the console: a length that is zero or not a multiple of 16 KiB and protection bits other than CPU read, write and execute areEINVAL; an alias or a map asking for more than the descriptor's maximum protection (write and execute counting as read) isEACCES, as FreeBSD 11'svm_map_protecttreats a protection pastmax_protection; a descriptor that is not a JIT object isEBADF. The name is ignored, the objects do not count against the flexible memory budget,mprotectof a view is not limited to the maximum protection, andmmapof the descriptors is unsupported, as forshm_open. On Linux thememfdis created withoutMFD_EXEC, which Linux 6.3 added and older kernels reject withEINVAL, and relies on the protection of the mapping: the flag only sets the file's execute permission bits, and the direct memorymemfdis mapped executable the same way; a host withvm.memfd_noexecset to 2 refuses the creation and the call throws - kqueue and kevent (libkernel) follow FreeBSD 11's
kern_event.c(kqueue_kevent,kqueue_register,kqueue_scan, the timer and user filters) over the event queue; nothing is measured on a console. The descriptor is a host null device registered as the kqueue:closeandsceKernelCloseclose the queue anddup/dup2of it throw, butread,writeandfstatact on the null device instead of failing withEOPNOTSUPPor reportingS_IFIFO. Read and write events are supported on guest sockets only, plusEVFILT_READon the read end of a pipe (see the kevent on a pipe entry) and the timer, user and vnode filters and a read event on the host stream on descriptor 0, which never triggers, asselectnever reports descriptor 0 readable; PPSA34015 registers descriptor 0 for reading in a new kqueue at start-up and does not check the result; other filters,EV_CLEARon a socket or a pipe,EVFILT_READon another kqueue, aNOTE_NSECONDSperiod above one second with a fraction (11.0 converts the fraction with the microsecond macro) and any change to an existing timer (11.0 keeps the armed expiry and applies the new period after it, 11.4'sfilt_timertouchrestarts it) throw.EV_DISABLE,EV_DISPATCHandEV_FORCEONESHOTfollow kern_event.c: a disabled knote stays registered, stops polling or firing and is not reported, a trigger raised while it is disabled is kept and reported afterEV_ENABLE, whereknotewould drop it,EV_DISPATCHreports once and then disables the knote, andEV_FORCEONESHOTis ignored on a new knote while on an existing one it setsEV_ONESHOTand fires the knote; a negativeneventsfails withEINVAL. A new user event keeps itsfflagsunmasked and is not triggered byNOTE_TRIGGER, becausefilt_userattachreads the already clearedkn_fflags. The timer limitkq_calloutmax(ENOMEM) is not enforced, and closing a kqueue wakes a thread waiting on it inkeventwithEBADF, wherekqueue_drainmakes the close wait for that thread. A level-triggered event is reported once per call and moved behind the other events, as inkqueue_scan; thesceKernel*Equeuecalls keep their own collection loop.NOTE_LOWATon a socket sets the low-water mark as FreeBSD 11'sfilt_soreadandfilt_sowritedo, and a later change of the knote replaces it askqueue_registerreplaceskn_sdata: a read event fires once the buffered bytes reach the mark, or at end of file or on a listening socket. A read mark above 1 is taken on stream sockets only: on a datagram socket Linux'sFIONREADreports the next datagram where FreeBSD'ssbavailcounts every queued byte, so it throws. A write event takes a mark of 1 only, where the host's writability stands in for one free byte, since the host does not report the send buffer's free space; other write marks and marks below 1, which make FreeBSD's read filter always fire, throw - EVFILT_VNODE, sceKernelAddFileEvent and sceKernelDeleteFileEvent (libkernel) watch the host file or directory behind the descriptor and turn host changes into FreeBSD 11 vnode hints, which
filt_vfsvnodeadds whole when they share a bit with the requestedfflags: a write that grows a file isNOTE_WRITE | NOTE_EXTEND, another writeNOTE_WRITE, a shrinking truncate or an attribute changeNOTE_ATTRIB, a higher link countNOTE_LINK, a lower one or a pending deleteNOTE_DELETE, a new pathNOTE_RENAME; in a directory, an entry created, removed or renamed isNOTE_WRITE, withNOTE_LINKfor a subdirectory, and on Linux an entry moved from or to another directory addsNOTE_EXTEND; inotify queues the two halves of a rename one after the other, so a watcher woken by the first can read it alone, and a half read without the other is classified only after agetdents64on the watched directory, which takes the directory lock thatrenameholds until both halves are queued (do_renameat2aroundvfs_renameand itsfsnotify_move,iterate_dir), an ordering taken from the kernel source, not from inotify(7), which promises none; a directory registered withNOTE_EXTENDon Windows is accepted, asvfs_kqfilterdoes not check it, but never receives it, because the directory handle cannot tell a move between directories from a removal plus an addition. FreeBSD's hints depend on the filesystem and the console's filesystems are not known: a growingftruncatereportsNOTE_WRITE | NOTE_EXTENDwherevop_setattr_postreportsNOTE_ATTRIB, and on Windowsutimeson a file reportsNOTE_WRITE. The three Windows notifications are told apart in the error, which names theparent,entriesorsubdirectorieswatch, the path it was given andGetLastError, but aFindFirstChangeNotificationthat fails still throws rather than returning an error to the guest: the EVFILT_VNODE attach path isvfs_kqfilter, which returnsEINVAL,ENOMEMor 0 afterfgethas turned a descriptor that is not open intoEBADF, and consults no directory permission, so no FreeBSD errno stands for a host that refuses to notify, and what the console does on such a host is not measured. Only theparentwatch can fail this way in practice:entriesandsubdirectoriesneed theFILE_LIST_DIRECTORYthat opening the watched directory already took. The parent handle produces no hint of its own, asRunHostWatchonly callsFindNextChangeNotificationon it, so a later change may leave the watch on its 50 ms poll instead of throwing; that is not done here. Windows usesFindFirstChangeNotificationon the parent directory (and on a watched directory itself) and comparesGetFileInformationByHandleExsnapshots at least every 50 ms, because a write through an open handle is reported only when the handle is flushed or closed, so file hints can arrive 50 ms late, and a file deleted while watched, which the POSIX delete of Windows 10 1903 moves into$Extend\$Deleteduntil its last handle closes, reportsNOTE_DELETEwithoutNOTE_RENAMEand keeps its handle without a parent directory watch, as that directory cannot be watched; Linux uses inotify on the file and is not run.NOTE_REVOKE,NOTE_OPEN,NOTE_CLOSE,NOTE_CLOSE_WRITE,NOTE_READ, unknown bits and descriptors other than files, directories, pipes and sockets throw.sceKernelAddFileEvent(eq, fd, watch, udata)andsceKernelDeleteFileEvent(eq, fd)take the OpenOrbis and SharpProspero signatures;watchis taken as thefflagsand the event is added withEV_CLEAR, neither confirmed on a title (feat(libkernel): implement kevent EV_DISABLE, EV_DISPATCH and EV_FORCEONESHOT) (fix(libkernel): follow kqueue_register for EV_FORCEONESHOT and modify flags) - sem_timedwait (libkernel) consumes an available count before checking the timeout and waits indefinitely for a null timeout, following FreeBSD 11's
sem_timedwait. Console behavior for null and invalid timeouts when a count is available is unverified. - tcsendbreak, tcdrain, tcflush, tcflow, tcgetpgrp, tcsetpgrp (libkernel) follow FreeBSD 11.4's
termios.c:tcflushanswersEINVALfor a queue other thanTCIFLUSH,TCOFLUSHorTCIOFLUSHandtcflowfor an action outsideTCOOFF..TCIONbefore the descriptor is looked at, and every other call fails as its terminal ioctl does on a descriptor that is not a terminal, with the errorstcgetattrgives, sotcsendbreaknever reaches its 0.4 s pause. FreeBSD'spipe_ioctlanswersTIOCGPGRPandTIOCSPGRPon a pipe or FIFO with the descriptor's SIGIO owner, which is not modelled, sotcgetpgrpandtcsetpgrpthrow on a host pipe or FIFO above descriptor 2; descriptors 0 to 2 are taken as non-terminals that are not pipes, whatever the host attaches to them, so a pipe the guest creates on a freed descriptor 0 to 2 getsENOTTYthere. The PS5libkernelis assumed to use this FreeBSD code, andtcdrainis not a cancellation point here - Windows file timestamps use the host filesystem's timestamp range and resolution.
utimes,futimes, andlutimesreject timestamps outside nonnegative signed 64-bit Windows file-time ticks withEINVAL, including exactly 1601-01-01 00:00:00 UTC, whose zero value means "leave unchanged" toSetFileTime. Directory timestamp updates resolve the current path of the opened handle. Filesystems with coarser timestamp resolution cannot retain every guest microsecond - EVFILT_VNODE on macOS (libkernel) registers
EVFILT_VNODEon a duplicate of the descriptor in a host kqueue, whose notes have FreeBSD's values, and drops theNOTE_LINKthat XNU adds toNOTE_DELETEwhen the watched file or directory is removed, since FreeBSD'svop_remove_postandvop_rmdir_postreportNOTE_DELETEalone. As on Windows, a directory registered withNOTE_EXTENDis accepted but never receives it: XNU reports a move between directories asNOTE_WRITEalone, withNOTE_LINKfor a subdirectory - Windows file identity (libkernel) -
statandfstatreport the volume serial number asst_devand the 64-bit file index folded into the guest's 32-bitst_ino(never 0), so two different files can in principle share anst_ino; the fold is not taken from a reference.st_nlinkis clamped to 65535, the width of the guest field. The call keeps the_wstat64values whenGetFileInformationByHandlefails - lstat of a Windows symbolic link (libkernel) reports a link whose target is relative, as
symlinkcreates them, with the link's own times,st_devandst_ino, modeS_IFLNK | 0777and the length of the targetreadlinkreturns asst_size. FreeBSD gives a new link0777less the creating process's umask, which the host does not keep, so the permission bits are not the console's. A link with an absolute target is reported the same way, using its print name, or its substitute name when the print name is empty, without a\??\or\\?\prefix. A directory junction is reported the same way when the name left after that prefix is a drive path (X:\...): the print name when that name is a drive path, otherwise the substitute name. A mounted volume, whose substitute name is\??\Volume{GUID}\, and any other reparse point throw, as forreadlink - setitimer (libkernel) follows FreeBSD's
sys_setitimerandkern_setitimer:EINVALfor timers other thanITIMER_REAL,ITIMER_VIRTUALandITIMER_PROF, and for a value, or the interval of a non-zero value, with a negative field,tv_usecof 1000000 or more, or more thanINT32_MAX / 2seconds; a null value reads the timer asgetitimerdoes, withEFAULTwhen the output is null too.ITIMER_REALshares the timer thread ofalarm, which clears its interval: a zero value disarms it, the previous value is returned, and each expiry re-arms it byit_interval. WithSIG_IGNan expiry is dropped. WithSIGALRMblocked it is left on the pending set ofsigtimedwait, askern_psignaldoes, wheresigwait,sigtimedwaitandsigwaitinfotake it; unblocking it runs a guest handler on the unblocking thread, and with the default disposition exits the process with status 142. Unblocked, the default disposition exits the process with status 142 and a handler throws, since guestSIGALRMhandlers are never run from the timer thread. The value is not raised totick / 16asitimerfixdoes, and an interval below 1 ms throws, since the console'shz, and so that rounding, is unknown (1 ms covers anyhzof 63 or more).ITIMER_VIRTUALandITIMER_PROFthrow when armed, as process CPU time is not accounted, and read and disarm as zero. PPSA99420 imports it and fails to load without it - On Linux, direct memory lives in one sparse memfd at file offset = physical address, so the GPU imports can share one udmabuf buffer object per 64 MiB of it. Released direct memory is not punched out of the file, because a live import of its chunk pins the pages and the GPU would keep reading the old ones: its pages stay committed (the process keeps the high-water mark of the direct memory it used) and are zeroed when the range is allocated again.
- system (libkernel) runs no command. It follows FreeBSD 11's
lib/libc/stdlib/system.cfor a sandbox without/bin/sh: a null command returns 1 unconditionally, as that version does, and any command returns the wait status of a child that could not exec the shell and called_exit(127)(0x7f00). Whether the console's application sandbox has no shell, and whethervforksucceeds there (FreeBSD returns -1 when it fails), is unverified - sceKernelWriteThrottlingStatus (libkernel) reports no write throttling: the host SSD has no write budget, so the first qword of the 32-byte status is set to
UINT32_MAXand the rest is zeroed; returnsEFAULT(0x8002000e) when passed a null status buffer pointer. PPSA17221 reads only that qword: it multiplies it by 64 KiB and compares the product with byte thresholds up to 0x4AF00000 to pick one of five tiers. When the call fails the title assumes 0x4B000001 bytes, which selects the same top tier asUINT32_MAX. What the value measures on hardware (a remaining budget or a rate) and the meaning of the other three qwords are unknown - Windows directory descriptors (libkernel) take one snapshot of the directory, on the first
getdents,getdirentriesor non-zerolseek(SEEK_SETorSEEK_CUR), and keep it until it is discarded;lseekto offset 0 only discards the snapshot, and the next read lists the directory again, so every rewind costs O(entries) on the next read.lseektakes the offset as an entry index andSEEK_CURwith offset 0 only reports the index.SEEK_ENDthrows because FreeBSD defines it relative to the directory size, which is not modelled. A negative result returnsEINVALand aSEEK_CURoffset that overflows returnsEOVERFLOW, as FreeBSDvn_seekreports them. Names whose UTF-8 encoding exceeds the one-byted_namlenlimit are not representable in this guest directory-record layout:getdentsreturnsENAMETOOLONGwithout consuming the entry. Repeated calls therefore report the same error and entries after that name cannot be reached through this descriptor; FreeBSD'sgetdentsbehavior for host filenames that exceed itsMAXNAMLENis not verified.getdirentriesstill reports 0 throughbasepinstead of the position of the block it read, so only 0 and values returned bylseekare valid offsets; FreeBSD offsets are filesystem cookies. Descriptors retain the opened directory's native handle across renames. Enumeration reloads and*atoperations resolve its current path, so a concurrent rename between resolution and the operation is not atomic. Enumeration of a removed directory and network-share path queries have not been verified - /dev/random and /dev/urandom (libkernel) opened for reading return a descriptor backed by the host null device whose reads are filled with host OS entropy (
getentropy,rand_son Windows); both paths behave the same, never block, and positioned reads ignore the offset (FreeBSDrandom(4)). Only the exact paths are recognised,fstat,lseekandmmapsee the null device, and opening them for writing throws - pthread_attr_getinheritsched, pthread_attr_setinheritsched, pthread_attr_getscope, pthread_attr_setscope, pthread_attr_getstackaddr (libkernel) follow FreeBSD 11 libthr (thr_attr.c, thr_create.c), not measured on the console: the scope defaults to
PTHREAD_SCOPE_SYSTEM(2), only it andPTHREAD_SCOPE_PROCESS(0) are accepted and other values returnEINVAL, and an inherit value other thanPTHREAD_INHERIT_SCHED(4) orPTHREAD_EXPLICIT_SCHED(0) returnsENOTSUP.pthread_attr_getstackaddrreturns the configured stack address pointer (null by default). A null output ofpthread_attr_getinheritschedthrows, since libthr writes through it unchecked. A thread keeps the scope it was created with, taken from the creating thread underPTHREAD_INHERIT_SCHED, andpthread_attr_get_npandscePthreadAttrGetreport it, but every guest thread is a host thread at system scope, so process scope changes nothing - Shared memory (libkernel) on Linux implements anonymous and named objects, bounded descriptor writes and seeks, and
shm_unlinkwith effective read/write permission checks from FreeBSD 11. Named paths up to 1023 bytes use/anyps5-shm-plus SHA-1; collisions are not detected, and the namespace is shared across processes in the host mount namespace. Named creation applies the guest umask without changing the host mask. Linux procfs and/dev/shmare required. Operations use an advisory directory lock; external host operations bypass it, unrelated objects serialize, and descriptor operations require two spare host descriptors and compare device/inode with the initial descriptor identity on both the pinned-object and ordinary-file fallback paths. Guest mappings support shared and private mappings of these descriptors through the allocation tracker and pin a duplicate descriptor during validation and mapping; mapping requires one spare host descriptor; file-backed mappings use the write-watch scan fallback because aliases and descriptor writes bypass per-mapping tracking. Named objects and file-backed mappings on Windows remain unsupported. Invalid non-null name pointers, console behaviour and title compatibility remain unverified. - sceKernelAddReadEvent and sceKernelAddWriteEvent (libkernel): a read event on a listening socket, here and in
kevent, reports the number of completed connections waiting to be accepted indata, as FreeBSD 11.4'sfilt_solistenreportsso_qlen: on Linux a TCP listener's accept queue fromTCP_INFO(tcpi_unacked, which Linux fills with the accept backlog for a listening socket), and elsewhere, or for a listener that is not TCP, 1 while a connection is waiting, as no host call reports that count. A write event reports the free space of the socket's send buffer indata, as FreeBSD 11.4'sfilt_sowritereportssbspace: on Linux the host'sSO_SNDBUFless the bytesSIOCOUTQcounts as queued, which include the host's per-packet overhead, so the space shrinks by somewhat more than the bytes sent. Windows has no query for that space and macOS is not wired up, so both report the wholeSO_SNDBUF, which does not shrink as data is queued. Only guest sockets are supported; other descriptors and a low watermark above 1 throw - close and setsockopt (libkernel) reach a libSceNet socket through hooks libSceNet registers, as
selectpolls them; the other libkernel socket calls do not. libSceNet numbers its sockets from 32 in its own table, so a CRT file descriptor and a socket can share a number;closethen closes the socket. On the console both are one kernel descriptor space - sceKernelPread, sceKernelPwrite, sceKernelPreadv, sceKernelPwritev (libkernel) on Windows transfer at the given offset with an overlapped
ReadFile/WriteFileand restore the descriptor's file pointer afterwards. Guest scalar transfers, positioned transfers, vectored transfers and seeks share a position lock across duplicated descriptors; a vector holds it for the whole operation. Independently opened descriptors and other processes do not share that lock, so vector transfers over their overlapping file ranges are not coordinated. Handles that are not disk files returnESPIPE. A pipe or consolereadvcan return a short read when no more input is available; each iovec is transferred separately, so a failure after the first transfer returns the bytes already done - sceKernelAioSubmitReadCommands, sceKernelAioSubmitWriteCommands and their Multiple variants (libkernel) on Windows transfer through the same positioned path as
sceKernelPread/sceKernelPwriteand restore the descriptor's file pointer afterwards; positioned transfers, plain transfers and seeks of one descriptor are serialized per descriptor (shared across duplicated descriptors), so a concurrent transfer observes a stable file pointer - sceKernelAioSetParam (libkernel) writes the five values into the 20-byte scheduling struct the first argument points to, following the declarations in Orbital, SharpProspero and ampr_emu. Whether the console checks the values is unknown, so a null struct, a scheduling window or delayed count limit outside 0..128, a split flag other than 0 or 1 and a split size or chunk size above 0x1000000 (SharpProspero's maximums) throw. The scheduling settings are not used:
sceKernelAioInitializeImplignores the struct and requests run synchronously on submit - sceKernelGetFsSandboxRandomWord (libkernel) returns a fixed directory name,
gkbHtXc3Wq. On the console the name is the process's randomized path (syscall 602,randomized_path), and the sandbox exposes system files under it, such ascommon/libandcommon/cert. Nothing is mounted under the name here, so/gkbHtXc3Wq/common/...resolves under the guest root like any other path and does not exist, except/gkbHtXc3Wq/common/cert/CA_LIST.cer, which SandboxCertificates.cpp serves from the host trust store - The sandbox CA list (libkernel),
/<sandbox word>/common/cert/CA_LIST.cer, is a PEM bundle of the host's trust anchors, written once per process tocert_cache/CA_LIST.cerin the run directory with the owner write bit cleared: on Windows the current user'sROOTstore, filtered through Disallowed and the combined EKU extension/property for TLS server authentication (Windows fetches missing roots on demand, so a root that was never fetched is absent), elsewhereSSL_CERT_FILEor the first of/etc/ssl/certs/ca-certificates.crt,/etc/pki/tls/certs/ca-bundle.crt,/etc/ssl/ca-bundle.pemand/etc/ssl/cert.pemthat exists. On macOS,/etc/ssl/cert.pemis a static snapshot and does not reflect roots added to or distrusted in the Keychain. Existing guest files, explicit aliases and blocked mounts take precedence over the provider. The PEM snapshot does not carry Windows CTL restrictions beyond EKU, native revocation policy or later trust-store changes. Without any certificate the path does not exist. The console file's format is unverified: only that bundled OpenSSL accepts a PEM bundle there is shown (ProsperoRadio PPSA99001 and SwanStation PPSA98510, #5001). The other entries of the console'scommon/certdirectory, the directory itself (it does not exist here) and the error a write open gets (EACCESfrom the host here) are unverified - getcontext and setcontext (libkernel) and makecontext (libSceLibcInternal) use FreeBSD 11's amd64
ucontext_t(mcontextat 0x10,mc_len800,uc_linkat 0x330,uc_stackat 0x338, 0x370 bytes), the layout homebrew built against FreeBSD's headers passes: PPSA99039's OpenSSLASYNC(crypto/async/arch/async_posix.c) captures into a 0x370-byte stack buffer.getcontextwrites the 0x330 bytes beforeuc_link, as FreeBSD'sUC_COPY_SIZE. The console's own layout for these functions is not verified; the exception handler's context is a different one (seesceKernelRaiseException) - getcontext and setcontext (libkernel) keep the 512-byte FXSAVE image in
mc_fpstate(x87, MXCSR, XMM0-15), so the upper halves of YMM0-15 are neither saved nor restored, andgetcontextleavesmc_xfpustateandmc_xfpustate_len0.getcontextsetsmc_fpformatandmc_ownedfpto FreeBSD's_MC_FPFMT_XMMand_MC_FPOWNED_FPU,mc_csandmc_ssto the host selectors, andmc_flags,mc_fsbaseandmc_gsbaseto 0.setcontextloadsmc_fpstatewhatevermc_fpformatandmc_ownedfpsay, and restores neither the segment selectors,mc_fsbaseandmc_gsbasenor the extended state, which FreeBSD restores whenmc_flagsasks for them - setcontext (libkernel) and longjmp (libc) on Windows set the thread's stack bounds in the TEB to the stack they switch to: a
makecontextstack, or else the committed region around the target stack pointer. A switch to any other guest stack, such as a context built by hand, gets the bounds of that region only - scePthreadAttrGet, pthread_attr_get_np and sceKernelIsStack (libkernel) report the stack of a host thread the library did not create, on Windows, from one allocation granule (64 KiB) above the bottom of its reservation when everything above that is committed read-write, as in a relinked executable's main thread and its default-size host threads (0x200000 committed of 0x210000 reserved), so the guest can protect the lowest reported pages, as HotSpot does for its stack guard pages in Java 25 LAN / DNS (PPSA99869). Any other such thread, and every such thread on Linux, gets its whole host stack: on Windows
mprotectcommits its reserved lower pages before protecting them and drops the guard flag of Windows' guard pages, and on Linux it cannot change the unmapped lower pages - sceKernelInstallExceptionHandler records the handler, but a host fault still ends the process: only signals raised with
sceKernelRaiseExceptionare delivered. Unity's PS5Util module installs a SIGUSR1 handler that readsuc_mcontext.mc_rsp(offset 0xf8 of the context) and raises SIGUSR1 on other threads to stop them for garbage collection - sceKernelRaiseException on Linux sends the host SIGUSR1, which the library takes over, to the target thread, so the handler runs wherever the thread was interrupted, emulator code included. On the console kernel objects are system calls and libthr defers signals while a thread holds its internal locks; here a handler that needs a host lock the interrupted code holds deadlocks, and a thread that the handler keeps waiting keeps its host locks. Host waits resume after the handler. The host calls Linux does not restart end early: sleeps that do not retry return before their time, and the poll of
sceNetEpollWaitand socket calls with a send or receive timeout returnEINTRto the guest - sceKernelRaiseException builds the handler's
ucontextwith FreeBSD's amd64 layout (16-byte sigmask,mcontextat 0x40,mc_rspat 0xf8); the console's layout is not verified beyond the fields PS5Util reads - sceKernelRaiseException does not consult the target thread's signal mask
- pthread_sigmask (libkernel) shares the process-wide mask of
sigprocmaskand_sigprocmask, which keeps only the first 32 signals; FreeBSD keeps a mask per thread - sceKernelRaiseException reaches a thread that waits in an alertable
TimedWaitwait through an APC. A thread blocked anywhere else, including all waits underAPS5_COARSE_TIMED_WAITSand host waits inside the emulator, is redirected and runs the handler only when that wait returns - sceKernelRaiseException on Windows runs the handler wherever the target thread was interrupted. It waits only while the thread is inside winpthreads or restoring a context through
NtContinue, and throws if that lasts 1 s, so a handler that calls into other host code the thread was interrupted in, such as the C runtime's heap or a lock of the emulator, can deadlock on it or reenter it. A thread that holds the address-wait lock ofsceKernelSyncOnAddressWait,sceKernelSyncOnAddressWakeand_umtx_op, or the park queue lock of the guest mutexes, gets the raise as an APC instead, which runs once it has released the lock or while it waits; the other locks of the emulator are not deferred this way - sigaltstack (libkernel) records each thread's alternate signal stack and reports it back, following FreeBSD's
kern_sigaltstack(EINVALfor flags other thanSS_DISABLE,ENOMEMbelowMINSIGSTKSZ2048), but handlers never run on it, soSS_ONSTACKandEPERMare never reported - sigpending (libkernel) reports the intersection of the process-wide pending set of
sigtimedwaitand the blocked mask of_sigprocmask, as FreeBSD'ssys_sigpendingintersects the pending list withtd_sigmask, and returns -1 withEFAULTfor a null set. Because_sigprocmaskkeeps only the first 32 signals, signals 33 to 128 are never blocked and never appear in the set, and settingSIG_IGN(orSIG_DFLon a signal whose default action is to ignore) does not clear a signal that was already marked pending. PPSA99420 imports it - scePthreadJoin, scePthreadCreate (libkernel) - on a PS5 (firmware 13.60) joining a thread a second time returns
ESRCH(0x80020003), as it does here, but a joined thread is kept untilscePthreadCreatepublishes over the same handle storage, so one host object and its handle are retained per distinct guest handle storage location for the lifetime of the process; a guest that joins into a fresh storage location every time keeps every one of them. A null entry crashed the payload and still throws. Joining the calling thread returnsEDEADLKand a zeroed attribute creates a default thread, as on the console.scePthreadDetachon a joined thread answersEINVAL, as FreeBSD does on a thread that is not joinable; the console answer for a detach after a join is unverified. Detaching a thread while another thread waits to join it returnsEINVAL, and a second concurrent join returnsEOPNOTSUPP, as FreeBSD 11.4'sthr_detach.candthr_join.ccheckjoiner; before this PPSA34015 stopped once withJoining guest thread: Access is deniedwhen a thread it was joining was released under the join, and which of the two calls released it was not captured. A detached thread is still released when it ends, so joining it after it ended reads freed memory. A handle value copied to a second storage and joined there retains the object under the original storage key, so a laterscePthreadCreateover that original storage releases an object the copy still points at, and the copy reads freed memory - Thread cancellation (libkernel) follows FreeBSD libthr (states and types 0/1 and 0/2,
PTHREAD_CANCELED1, the_pthread_cleanup_infonode layout), not measured on the console. A request is acted on only inscePthreadTestcancel, the condition variable,semandscePthreadSemwaits,scePthreadJoinand the sleeps, and their POSIX names; file and socket calls are not cancellation points. A sleep is not interrupted: the thread exits when it ends. The asynchronous type acts like the deferred one, except at the two set calls. - cpuset_setaffinity (libkernel) - the calling thread, another live guest thread selected by its guest TID, or the calling process is handled; other levels (ROOT/CPUSET) and other scopes (CPUSET/JAIL) throw as unmodelled, invalid levels or scopes return
EINVALand unknown thread or process IDs returnESRCH, following FreeBSD 11.4 thread selection. Bits outsidesceKernelGetAvailableCpumaskare dropped, and a mask with none of those bits fails withEDEADLK, as FreeBSD does when the new set has no CPU in common with the parent set. Guest-TID lookup and the update hold the thread registry lock so a join cannot release the target during access. Affinity is recorded for guest CPU reporting and child inheritance, not applied to host scheduling; PS5 behaviour has not been measured - cpuset_getaffinity (libkernel) - the calling thread, another live guest thread selected by its guest TID, or the calling process is handled; other levels (ROOT/CPUSET) and other scopes (CPUSET/JAIL) throw as unmodelled, invalid levels or scopes return
EINVALand unknown thread or process IDs returnESRCH, following FreeBSD 11.4 guest-TID selection, not measured on PS5. For the process the mask is the union of the affinities of the live guest threads, as FreeBSD 11.4 ORs the mask of every thread in the process; threads the guest did not create through libkernel are not included, and the console result is unverified - pthread_setaffinity_np (libkernel) - follows FreeBSD 11.4 libthr over
kern_cpuset_setaffinityfor the calling thread and any live guest thread: a size other than the 32-bytecpuset_treturnsERANGE, an unknown or finished threadESRCH. Bits outsidesceKernelGetAvailableCpumaskare dropped and a mask with none of those bits returnsEDEADLK, ascpuset_setaffinitydoes; the affinity is recorded on the guest thread and not applied to the host thread, and the console result is unverified - dup and dup2 (libkernel) throw for the combinations they do not model:
dup2of a guest socket onto descriptors 0 to 2, whose host streams libc writes without the socket table, and on Windows duplicating a directory descriptor. Guest sockets share one socket object between the duplicates; the numbers come from the host descriptor table like those of files. The errno values for descriptor exhaustion are not verified against the console. - waitpid (libkernel) returns -1 with
ECHILD(EINVALfor unknown options): nothing here creates a child process; child processes would need a realwaitpid. - execv (libkernel) throws: replacing the process image is not supported, the same as
execvp. PPSA97358 imports it and fails to load without it. - execve (libkernel) throws like
execvp: replacing the process image is not supported. The console behavior for invalid arguments and executable path lookups is not modeled. - sysarch (libkernel) -
AMD64_GET_FSBASE,AMD64_GET_GSBASEandAMD64_SET_GSBASEfollow FreeBSD 11's amd64sysarchon Linux x86-64 hosts, through the host'sarch_prctl. The GS base belongs to the calling thread, a base at or above0x800000000000(VM_MAXUSER_ADDRESS) returnsEINVAL, and a null argument returnsEFAULT.AMD64_SET_FSBASEthrows, since guest and host code share the thread's FS base for their TLS. The other operations, and every operation on other hosts (Windows keeps its TEB in GS), throw. What a new thread's GS base starts as is not modelled: Linux threads inherit their creator's - kern.proc.pathname (libkernel) returns
/app0/eboot.binfor pid -1 and the calling process, following FreeBSD'ssysctl_kern_proc_pathname; the exact pathname on retail consoles remains unverified. - machdep.cacheline_size (libkernel) returns -1 with
ENOENT, matching FreeBSD amd64 where this sysctl OID is not defined. - hw.pagesize (libkernel) reports the system page size (16384 bytes, PS5_PAGE_SIZE) as an integer through sysctl MIB {6, 7} (CTL_HW, HW_PAGESIZE) and the "hw.pagesize" sysctlbyname lookup, following FreeBSD's
hw.pagesize, a read-onlySYSCTL_INTofPAGE_SIZE. The value on a PS5 follows the 16 KiB page size the rest of libkernel uses and is unmeasured. A write returns -1 with EPERM. - hw.model (libkernel) reports the host CPU's name, built as FreeBSD 11 builds
hw.model: the CPUID brand string (leaves 0x80000002 to 0x80000004) without leading spaces (identcpu.c). Trailing padding stays, as it does there, so an AMD host's name ends in spaces. The name a PS5 reports is unknown. A write returns -1 withEPERM. A host without an x86 CPUID brand string, such as an Apple Silicon Mac, throws. PPSA99203 only prints it ([console] hw.model: %s) - Host CPU check (libkernel) - when libkernel loads on a Linux or Windows x86-64 host, it logs which of the PS5 CPU's user-mode extensions CPUID reports and throws, naming the host CPU, if any extension is missing that LLVM selects from plain C/C++ for the PS5's Zen 2 (
-march=znver2, feature list): SSE to SSE4.2, SSSE3, POPCNT, LZCNT, BMI1, BMI2, MOVBE, CMPXCHG16B, LAHF/SAHF, AVX, AVX2, FMA, F16C, and the operating system's AVX state in XCR0 (#3719). SSE4A, CLZERO and SHA, which--to-intelconverts, and AES, PCLMULQDQ, RDRAND, RDSEED, ADX, CLFLUSHOPT, CLWB, RDPID, XSAVEOPT, XSAVEC and PREFETCHW, which compilers emit only for intrinsics or explicit prefetches, are logged but not required: a title that executes one of them on a host without it still stops with an illegal instruction. macOS hosts are not checked: some Rosetta versions execute extensions without reporting them through CPUID, but support differs by host. ANDN and RDRAND fault on an M1 with macOS 15.3.1; see the Rosetta conversion limits - machdep.tsc_freq (libkernel) reports the host TSC frequency that
sceKernelGetTscFrequencymeasures, as a 64-bit value like FreeBSD 11'ssysctl_machdep_tsc_freq. FreeBSD lets root write it to change the TSC timecounter's frequency; a write here returns -1 withEPERM. PPSA99203 reads it at start-up and measures the TSC itself when the value is 100 MHz or less - getrlimit (libkernel) -
RLIMIT_CPU,RLIMIT_FSIZE,RLIMIT_CORE,RLIMIT_SBSIZE,RLIMIT_VMEM/AS,RLIMIT_NPTS,RLIMIT_SWAP,RLIMIT_KQUEUESandRLIMIT_UMTXPreport infinity as in FreeBSD's process-0 defaults, andRLIMIT_DATAmirrors the host limit.RLIMIT_NOFILEreports, as both soft and hard limit, the number of descriptors after which AnyPS5 itself fails withEMFILE: on Windows the C runtime's 8192 low-level descriptors (_setmaxstdio), which guest files, pipes and sockets all take, and on Linux the host'sRLIMIT_NOFILE, whose soft limit libkernel raises to the hard limit at load. The console's limit is not known; XPSemu (PPSA97358) reads it in QEMU'sos_setup_limitsand raises the soft limit to the hard one when they differ.RLIMIT_STACK,RLIMIT_RSS,RLIMIT_MEMLOCKandRLIMIT_NPROCthrow because no console values are established. - setrlimit (libkernel) validates the resource and input in FreeBSD order, maps negative limits to infinity, and lowers a soft limit above the hard limit. A request succeeds when its normalized values match
getrlimit, or forRLIMIT_NOFILEwhenrlim_maxdoes not exceed the current hard limit andrlim_curdoes not exceedrlim_max, storing the updated limits for subsequentgetrlimitqueries; descriptor allocation enforcement of the lowered soft limit is not implemented, and changing other limits throws because guest limit state is not stored. Java 25 LAN / DNS (PPSA99869) setsrlim_cur = MIN(0x100000, rlim_max)during VM initialization - execvp (libkernel) throws: replacing the process image is not supported. PPSA85300 imports it and fails to load without it; its
forkandvforkfail withENOSYS(libc_missing.c), so the child process that would call it is never created. - nmount (libkernel) throws: mounting file systems is not supported. Prospero Themes (PPSA90210) imports it and fails to load without it; it opens its theme library without calling it, and its strings (
/dev/ssd0.system_ex,/system_ex,exfatfs,fstype,fspath,from,ignoreacl) point to remounting the console's/system_expartition to write theme files there. - socketpair (libkernel) - the pair is the host's
AF_UNIXpair on Linux and, on Windows, a loopback TCP connection forSOCK_STREAMor two connected UDP sockets forSOCK_DGRAM;SOCK_SEQPACKETthrows on Windows. A datagram sent to a full receiver getsENOBUFSon the console, waits or getsEAGAINon Linux and is dropped on Windows, and on Windows a datagramrecvafter the peer closed reportsECONNRESETinstead of the console'sENOTCONN, and a streamsendafter the peer closed reportsECONNABORTEDinstead of the console'sEPIPE.readandwriteon a pair end go to the host like on every guest socket.getpeernameon a pair end succeeds after the peer closed, where the console returnsENOTCONN, andconnect,bindandsendtowith an address on a pair end report an address-family error instead of the console'sEISCONN. On Windowspollon a stream end reportsPOLLHUPwithoutPOLLINafter the peer'sshutdown(SHUT_WR)orclose, where the console reportsPOLLIN, withPOLLHUPonly afterclose - UDP recv/recvfrom and sceNetRecv/sceNetRecvfrom translate Windows WSAEMSGSIZE into the copied byte count for datagram sockets, including MSG_PEEK, and retain the sender address. This follows FreeBSD 11.4 recv(2) and Winsock's documented truncated-datagram behavior; console behavior and existing title impact are unverified.
- MSG_DONTWAIT (libkernel) in
send,sendto,sendmsg,recv,recvfromandrecvmsgmakes that one call non-blocking through the host'sMSG_DONTWAIT, as FreeBSD 11.4'ssoreceive_genericreturnsEWOULDBLOCKfor it (sys/kern/uipc_socket.c). Winsock has no per-call non-blocking flag, so on Windows these calls still fail withEOPNOTSUPPfor it - send, sendto (libkernel) - send and sendto without an address on an unconnected datagram socket map Windows
WSAENOTCONNto FreeBSD'sEDESTADDRREQ(39). - read, write and vector I/O (libkernel) on a guest socket use
recvandsendwithout flags;sceKernelReadvandsceKernelWritevgather or scatter through one socket operation, preserving stream short reads and datagram boundaries. The positionedpread,pwrite,preadvandpwritevforms, and theirsceKernelforms, returnESPIPEfor an open guest socket andEBADFfor a closed one.EPIPEfrom a write raises the host'sSIGPIPEon Linux unless the socket hasSO_NOSIGPIPEset, and the guest cannot ignore it:signal(SIGPIPE, SIG_IGN)andsigactionrecord the disposition, as they do for every number from 1 to 128, but only the six signals with host equivalents (2, 4, 6, 8, 11 and 15) take effect on the host or are delivered, so the hostSIGPIPEis not stopped; nothing is raised on Windows; awritelonger thanINT_MAXbytes isEMSGSIZEwhere the console sends it. AIO on a guest socket still goes to the host, which has no such descriptor - Socket descriptor numbers (libkernel, libSceNet) -
socket,accept,socketpair,dup,sceNetSocketandsceNetAccepttake the lowest free number of the host descriptor table that files use, as FreeBSD 11.4fdalloctakes the lowest free entry of the one process table (kern_descrip.c), so every number stays belowFD_SETSIZE(1024) while fewer than 1024 descriptors are open. The number holds a zero-accessNULhandle on Windows and a duplicate of the host socket on Linux, and the socket tables map it to the socket. The host table also holds the runtime's own files, so the numbers differ from the console's; the first numbersocketandsceNetSocketreturn on a console and itsgetdtablesizeare not measured. Paths that bypass the socket tables see the placeholder: libcfdopenon a socket opens it, and libkernelcloseof a libSceNet socket releases the number but leaves the libSceNet socket open. Palworld (PPSA23126), The Mound (PPSA26223) and Dying Light 2 (PPSA02262) set socket bits in a 1024-bitfd_set - fstat on a guest socket (libkernel) fills the fields FreeBSD 11's soo_stat and pru_sense_null fill and zeroes the rest: S_IFSOCK, the read bits while the receive side is open or data is queued, the write bits while the send side is open, st_size and st_blksize. Only this process's shutdown closes a side here; a peer's shutdown, close or reset does not clear the bits as FreeBSD's socantrcvmore and socantsendmore do. st_size is the host's FIONREAD, which for a Linux datagram socket is the size of the next datagram where FreeBSD counts every queued byte, and st_blksize is the host's SO_SNDBUF, which Linux reports as twice the size that was set. A socketpair end reports st_dev and st_ino 0, where FreeBSD's uipc_sense reports NODEV and a per-socket number
- poll (libkernel) waits on guest sockets and pipes, through
WSAPollon Windows andpollon Linux; thepollfdlayout and flag values are FreeBSD's. A libSceNet socket joins the same host wait through the native socket libSceNet lends to libkernel.WSAPollrejectsPOLLPRI, so on Windows a socket'sPOLLPRIis asked asPOLLRDBANDandPOLLINasPOLLRDNORM; Winsock reports urgent data until it is read withMSG_OOB, where FreeBSD 11.4'ssopoll_genericstops once the normal stream passes the mark. GuestsendandrecvrefuseMSG_OOBwithEOPNOTSUPP, so urgent data can only come from an outside peer and Windows keeps reportingPOLLPRIafter the stream passes the mark. A pipe follows FreeBSD'spipe_poll: the read end is readable when data is buffered, either end reportsPOLLHUPonce the other end is closed, with the requestedPOLLINandPOLLRDNORMbits and withoutPOLLOUT, whichpollscanclears, and the write end is writable whenPIPE_BUF(512) bytes are free on Windows, or when the host kernel says so on Linux.POLLOUTon a read end throws: FreeBSD opens both ends for reading and writing and reports it writable, and host pipes are one-way. Windows pipes cannot be waited on, so while a pipe is polledpollchecks it withNtQueryInformationFileand waits on the sockets in slices of 1 ms. A regular file, a directory or a file read from app0.zar is always ready forPOLLIN,POLLRDNORMandPOLLOUTand never forPOLLPRI,POLLRDBANDorPOLLWRBAND, as FreeBSD 11.4'svop_stdpollandvop_nopollanswer every eventpollaccepts; which of the two the console's filesystems use is not known, and they agree on these events./dev/randomand/dev/urandomfollow FreeBSD 11.4'srandomdev_pollfor a seeded device: a request withPOLLINorPOLLRDNORMgets only those bits back, and any other request gets every requested bit. Open descriptors 0 to 2 follow the console modelselectuses: descriptor 0 never reports an event and descriptors 1 and 2 reportPOLLOUT, whatever the host attaches to them. A closed descriptor reportsPOLLNVAL, and an open descriptor that is neither a guest socket, a libSceNet socket, a pipe, a file, a directory, the random device nor descriptor 0 to 2 (other devices, a kqueue) throws, as do event bits other thanPOLLIN,POLLPRI,POLLOUT,POLLRDNORM,POLLRDBANDandPOLLWRBAND(thePOLLERR,POLLHUPandPOLLNVALbits are ignored inevents), an infinite timeout with no socket or pipe to wait on. A closed socket descriptor reportsPOLLNVAL; the console's limit on the descriptor count is unknown and not checked - kevent on a pipe (libkernel) -
EVFILT_READon the read end of a pipe follows FreeBSD 11'sfilt_piperead:datais the number of buffered bytes, andEV_EOFis set once the write end is closed. Windows pipes cannot be waited on, so while a kqueue waits on a pipe it checks it withNtQueryInformationFileand waits on its sockets in slices of 1 ms; a wait with no pipe is not sliced.EVFILT_READon the write end of a pipe throws: FreeBSD'spipeopens both ends for reading and writing (kern_pipe), and host pipes are one-way.EVFILT_WRITEon a pipe throws too, andNOTE_LOWATon a pipe is ignored, asfilt_pipereadignores it. - Socket error numbers (libkernel) - a host socket error becomes the FreeBSD 11
errno.hnumber of the same name; the numbers are not checked on the console. WinsockWSAESHUTDOWNbecomesEPIPE, as a send aftershutdowndoes on FreeBSD, and a host error with no FreeBSD socket counterpart becomesEIO. A nonblockingconnectin progress reportsEINPROGRESSon Linux butEAGAINon Windows (WSAEWOULDBLOCK), where FreeBSD reportsEINPROGRESS. On Windows a refused nonblockingconnectis never reported (SO_ERRORis unsupported), so a repeated one keeps reportingEALREADYwhere FreeBSD reportsECONNREFUSED. A receive aftershutdown(SHUT_RD)reportsEPIPEon Windows where FreeBSD returns 0 - getsockopt, setsockopt (libkernel) on a guest socket handle the SOL_SOCKET level and
TCP_NODELAY, after FreeBSD 11.4'ssosetoptandsogetopt:SO_REUSEADDR,SO_KEEPALIVE,SO_DONTROUTE,SO_BROADCAST,SO_OOBINLINEandSO_LINGERact on the host socket, andSO_ERROR,SO_TYPE,SO_ACCEPTCONN,SO_SNDBUFandSO_RCVBUFare read. A boolean option reads back as its own bit, as FreeBSD returnsso_options & option. Winsock does not clear a socket's pending error when it is read, so on Windows the library reports a pending error once and shows it again only after a newconnect; a second failure with the same code on the same socket without aconnectin between would be hidden.SO_SNDBUFandSO_RCVBUFread the host's value, which is not the size FreeBSD reports (Linux doubles it).SO_NOSIGPIPEis kept per socket and reads back as its bit. On Linux asend,sendto,sendmsgorwriteon that socket passesMSG_NOSIGNALto the host, askern_senditandsoo_writeleave out the signal. An accepted socket takes it from its listener, assonewconncopies the options.SO_NBIO(0x1200) is the console's own option, not FreeBSD's. It switches the socket between blocking and non-blocking asFIONBIOdoes and reads back as 0 or 1, assceNetSetsockoptandsceNetGetsockopttreat it here; what the console reads back for it is not measured.TCP_NODELAY(IPPROTO_TCP, 1) is set on the host socket and reads back asTF_NODELAY(4), astcp_default_ctloutputreturns it. TheIPPROTO_TCPlevel on a datagram orAF_UNIXsocket fails withEINVAL, asip_ctloutputanduipc_ctloutputanswer it, and an accepted socket takesTCP_NODELAYthrough the host.SO_SNDTIMEOandSO_RCVTIMEOfollowsosetoptandsogetopt. A 16-bytetimevalwith a negative field or 1000000 microseconds or more fails withEDOM, and more thanINT32_MAXseconds becomes the longest wait. The value is kept and read back through FreeBSD's 32.32 fixed-point conversion (tvtosbt,sbttotv), so half a second reads back as 499999 microseconds and one microsecond as 0. Arecv,recvfrom,recvmsg,readorreadv, and asend,sendto,sendmsg,writeorwritev, on a blocking socket then waits that long for the host to report the socket readable or writable, and fails withEWOULDBLOCK; a call withMSG_DONTWAITdoes not wait. An accepted socket takes both from its listener (sonewconn). The wait covers only the first transfer: once the socket is writable the host send runs without a limit, where FreeBSD stops a send that cannot finish and returns the bytes sent so far, and a receive whose data another thread takes between the wait and the host call blocks again.sendfileignores the send timeout. The host wait has millisecond steps, so a shorter timeout lasts up to a millisecond longer. A null value fails withEINVAL, where FreeBSD'skern_setsockoptreturnsEFAULT. Every other SOL_SOCKET option (SO_DEBUG,SO_USELOOPBACK,SO_REUSEPORTandSO_TIMESTAMP, which FreeBSD stores and no title has been seen to set, the low-water marks among them), every otherIPPROTO_TCPoption (TCP_MAXSEG,TCP_KEEPIDLE,TCP_KEEPINTVLandTCP_KEEPCNTamong them) and every other level (IPPROTO_IP,IPPROTO_IPV6) fail withENOPROTOOPT. None of this was measured on a console. - connect to AF_UNSPEC (libkernel) dissolves a datagram association as FreeBSD 11.4's
udp_disconnect()does, which resets the local address to the wildcard address and keeps the port. On Linux a socket bound to an explicit port keeps its local address as well (127.0.0.1:<port>stays127.0.0.1:<port>), because a bound socket cannot be bound again; one bound to port 0 is bound again to the wildcard address with the port it had - connect (libkernel) -
connectto port 0 on an unconnectedAF_INETorAF_INET6stream or datagram socket fails withEADDRNOTAVAIL(49), as FreeBSD 11.4'sin_pcbconnect_setup()andin6_pcbconnect()do. A connected stream socket keeps its hostEISCONNanswer. Known gaps: on an already connected datagram socketconnectto port 0 keeps the old peer, where FreeBSD'ssoconnect()disconnects first and then fails with 49;sendtoandsendmsgto port 0 are sent, whereudp_output()fails with 49 on the unbound path but FreeBSD can send to port 0 from a socket bound to a specific address and port. The answers are taken from the FreeBSD source and are not measured on a console. sceNetConnect is not changed - msync (libkernel) follows FreeBSD 11's
kern_msyncandvm_map_sync, not measured on the console: the address is rounded down to the page, a range that wraps isEINVAL, as isMS_ASYNCwithMS_INVALIDATE, and a range whose start or any page is not mapped (assceKernelVirtualQueryreports it, reservations included) isENOMEM; a page unmapped from inside a larger mapping still counts as mapped there, sincesceKernelVirtualQuerykeeps reporting the whole mapping. The guest has no file mappings, so a successful call writes nothing back.MS_INVALIDATEon a mapped range throws: FreeBSD returnsEBUSYwhen the range holdsmlocked pages, and locked pages are not tracked - madvise (libkernel) accepts
MADV_PROTECT(10) as a no-op hint without modifying process protection. FreeBSD 11'skern_madvise(sys/vm/vm_mmap.c) handles it before the address check, ignores the address and length, and callskern_procctl(..., PROC_SPROTECT, PPROT_SET), which requiresPRIV_VM_MADV_PROTECTand fails withEPERMwithout it. Returning 0 unconditionally assumes the calling process is privileged. Console behaviour is unverified - mincore (libkernel) follows FreeBSD 11's
sys_mincore(sys/vm/vm_mmap.c), not measured on the console: the address is rounded down to the page, length 0 returns 0, a null vector over a mapped range returnsEFAULT, an overflowing range or any unmapped page in the range (as reported bysceKernelVirtualQuery) returnsENOMEM. For valid ranges, a page reportsMINCORE_INCORE(0x1) whensceKernelVirtualQueryreports it committed and 0 when it is only reserved; actual residency, referenced and modified bits are not modelled. Length 0 returns 0 when the start page is mapped andENOMEMotherwise, and the range and mapping checks come before the null vectorEFAULT, as inkern_mincoreJava 25 LAN / DNS (PPSA99869) imports it - mlockall, munlockall (libkernel) -
mlockallreturnsEINVALfor zero or unknown flags. ValidMCL_CURRENTthrows because it would lock host stacks, heaps, modules and driver mappings along with guest memory;MCL_FUTUREthrows because later mappings are not tracked for locking. FreeBSD 11.4'ssys_munlockall(sys/vm/vm_mmap.c:1133-1165) checkspriv_check(td, PRIV_VM_MUNLOCK)first (returningEPERMwithout it) and unwires the user map (vm_map_unwireovervm_map_min...vm_map_maxwithVM_MAP_WIRE_HOLESOK). Returning 0 unconditionally assumes the calling process is privileged.munlockallunlocks memory locked bymlock(VirtualUnlockon Windows, toleratingERROR_NOT_LOCKEDfor pages never locked, andmunlockallon Linux). On main, XPSemu (PPSA97358) fails to load at these imports (mlockall #4386, munlockall); their calls in QEMUos-posix.cexecute only when-overcommit mem-lock=onis enabled. The call has not been observed in a run with this implementation. - socket (libkernel) creates only
SOCK_STREAMandSOCK_DGRAMsockets inAF_INETandAF_INET6. A type that FreeBSD 11.4's inet domain has no protocol for in any configuration (in_proto.chas noSOCK_RDMentry and no unknown type) fails withEPROTOTYPEwhen the protocol is 0 andEPROTONOSUPPORTotherwise, assocreatedoes.SOCK_RAW, whichin_proto.calways has (socreatecreates it for a privileged process), andSOCK_SEQPACKET, which it has only in a kernel built withSCTP, fail withEPROTONOSUPPORT; whether the console allows either is not known.SOCK_DGRAMwithIPPROTO_UDPLITEandAF_UNIX(socketpairsupports it) fail withEPROTONOSUPPORTandEAFNOSUPPORTwhere FreeBSD creates the socket - sigtimedwait (libkernel) waits on a process-wide pending set instead of throwing: a signal raised while blocked stays pending and is taken by
sigwait,sigwaitinfoorsigtimedwait, delivered to the handler when_sigprocmaskunblocks it, or delivered bypause. The set covers all 128 signals and the lowest number that is pending and in the set is selected (FreeBSD specifies the lowest number only among real-time signals). Selection,EAGAINon timeout,EINVALfor atv_nsecoutside 0 to 1000000000 while nothing is pending andNULLtimeout blocking indefinitely follow FreeBSD's sigtimedwait(2).sigwaitreturns 0 with the number in its output, as POSIX specifies, andsigwaitinfoandsigtimedwaitreturn the number, as FreeBSD does. The guestsiginfo_tfollows FreeBSD 11'ssys/sys/siginfo.h: 80 bytes,si_signoat 0,si_codeat 8,si_pidat 12,si_uidat 16,si_addrat 24,si_valueat 32 andsi_bandat 40; onlysi_signo,si_code(SI_USER) andsi_pid(the guest pid) are written,si_uidstays 0 because no uid is modelled, and the console's layout is not verified. PPSA85300 importssigtimedwaitand fails to load without it; its only call site is VLC'svlc_writev(filesystem.c), which polls with a zero timeout to drain aSIGPIPEonly afterwritevfails withEPIPE. The waits are not cancellation points and an unblocked caught signal does not interrupt them withEINTR, where FreeBSD'ssigwaitinfoandsigtimedwaitmay return it.SIGPIPEhas no host equivalent and is never raised by a host write here (seeread, write and vector I/O), so VLC's drain finds nothing - pause, sigsuspend, sigwait, sigwaitinfo (libkernel) take the pending set of
sigtimedwaitinstead of throwing.pausewaits for a pending signal that is not blocked and whose disposition can be delivered, runs its handler on the calling thread and returns -1 withEINTR; an ignored signal is discarded and the wait continues, and it never returns for a signal whose handler cannot run. A signal whose registered handler hasSA_SIGINFOstill throws, and one whose disposition is the default action and that has no host equivalent (every signal except 2, 4, 6, 8, 11 and 15) is neither delivered nor discarded: it stays pending until a wait takes it, where FreeBSD runs the default action, which terminates the process for most of them. A pendingSIGALRMwith the default disposition exits the process with status 142 once it is unblocked, as FreeBSD's default action does. PPSA68091, PPSA99169, PPSA99420 and PPSA97358 import them and fail to load without them. PPSA68091 callspauseat one site (0x8F7266), in a loop that waits while a global holds another thread'serrnoaddress. PPSA99169 only stores the address ofpausein a table of symbol addresses (0x30DB1). PPSA99420 callspauseat 0x3BD80F6 and 0x3C9E220,sigwaitat 0x3C9E4C7 andsigwaitinfoat 0x3C9E588. PPSA97358 callssigwaitfrom QEMU'ssigwait_compatthread (compatfd.c), whichqemu_signal_init(main-loop.c) starts forSIGIO,SIGALRMandSIGBUSwhen the main loop is created, so it reaches the wait at start-up, and from the vCPU thread of the dummy accelerator (dummy-cpus.c).sigsuspendswaps the process-wide mask of_sigprocmaskfor its argument, waits aspausedoes through the same helper, then restores the mask through_sigprocmask, and returns -1 withEINTR; a null set throws, as the console faults in libthr instead of returningEFAULT. All four words of the argument replace the mask and are restored afterwards, as in_sigprocmask. Java 25 LAN / DNS (PPSA99869, #5501) imports it and fails to load without it, aftersysctlnametomib,fegetenvandmincore. None of the five waits is a cancellation point - getsid (libkernel) returns the calling process ID for pid 0 and the calling process, and
ESRCHfor other process IDs, assuming the single guest process is its own session leader. PPSA74126 imports it and fails to load without it - sigaction (libkernel) records the handler, flags and mask of every signal from 1 to 128 and reports them back, following FreeBSD's
sigaction(EINVALfor other signals, and for a handler onSIGKILLorSIGSTOP);signalrecords its handler withSA_RESTART, as FreeBSD's libc does. Only handlers withoutSA_SIGINFOon the signalssignalalready delivers (SIGINT,SIGILL,SIGABRT,SIGFPE,SIGSEGV,SIGTERM) run, the same waysignalhandlers do; their mask and flags are not applied.SA_SIGINFOhandlers are recorded but never called, since that needs FreeBSD'ssiginfo_tanducontext_t, and neither are the handlers of other signals (SIGBUS,SIGHUP, ...), whether they are set bysigactionor bysignal, which records them too (every number from 1 to 128, withSA_RESTARTand an empty mask replacing whatsigactionstored;SIGKILLandSIGSTOPaccept only the default handler in both calls, as in FreeBSD'skern_sigaction).raiseandkillaccept every number from 1 to 128 now: a blocked one stays pending on the set ofsigtimedwait, an unblocked one whose handler is registered runs it on the calling thread, and an unblocked one that has no host equivalent and no handler still fails withEINVAL, where FreeBSD runs its default action.raiseof anSA_SIGINFOhandler still throws instead of terminating - kill, killpg (libkernel) follow FreeBSD's
kern_killandkillpg1for the only guest process:EINVALfor signals outside 0 to 128 and for negativekillpgprocess groups, signal 0 only checks that the target exists, and its own pid, 0 and minus its own pid (the guest is taken to lead its own process group) signal it throughraise, so they shareraise's limits above. Any other pid, and -1, which skips the caller, reportESRCH, soEPERMis never reported - sceKernelFcntl (libkernel) runs the POSIX
fcntlbelow and returns its guest errno asSCE_KERNEL_ERROR_*, the form the othersceKernelfile exports use. The return values and error codes are unverified on a console - fcntl (libkernel) follows FreeBSD 11.4's
kern_fcntlandkern_dupon file, directory, pipe and character device descriptors; a socket keeps the libSceNet path (F_GETFL,F_SETFLwithO_NONBLOCK,F_GETFDandF_SETFD,EINVALfor every other command, see the next entry). The close-on-exec flag and the status flags live in a guest table, the status flags shared bydup,dup2andF_DUPFDas in an open file description; a descriptor the table has not seen, such as 0 to 2, starts from the host's flags, since the console's are not known.F_GETFLtakes the access mode from the host (the granted access of the handle on Windows), so pipe ends reportO_RDONLYandO_WRONLYwhere FreeBSD's bidirectional pipes reportO_RDWR.F_DUPFDandF_DUPFD_CLOEXECreturn the lowest free host descriptor at or above the start, so the bound is the host table (EMFILEon Windows,EINVALpastRLIMIT_NOFILEon Linux), not the console's unknownRLIMIT_NOFILEandkern.maxfilesperproc.O_NONBLOCKswitches a host pipe (PIPE_NOWAITon Windows, where an empty read and a full write then reportEAGAIN) and is only recorded on files, directories and the random device, as their reads never block; on a Windows console or other character device it throws. ChangingO_APPENDon a Windows file, changingO_FSYNCon a file,O_ASYNCon a pipe or character device (SIGIOis not delivered),F_GETOWN,F_SETOWNandF_SETLK_REMOTEthrow;O_DIRECT,F_RDAHEADandF_READAHEADare recorded as the hints they are. Record locks (F_GETLK,F_SETLK,F_SETLKWand their forms withoutl_sysid) use the host's POSIX locks on Linux and macOS.F_GETLKwithF_UNLCKasks the host withF_RDLCK: as inlf_blocksonly another owner's overlapping write lock conflicts with either request. On Windows they followkern_fcntlandlf_advlockasyncfor the one guest process, which owns every lock:EOVERFLOWfor anSEEK_CURstart pastOFF_MAX, thenEBADFforF_RDLCKwithout read access orF_WRLCKwithout write access andEINVALfor another type, thenEINVALandEOVERFLOWfor the range; aslf_blocksnever lets locks of one owner conflict, whichever descriptor or thread asks,F_SETLKandF_SETLKWthen succeed without waiting andF_GETLKreportsF_UNLCK, so no lock list is kept and merging, splitting and the release on close have nothing to change. Nothing is held on the host, since Windows byte-range locks are mandatory and would fail the guest's own reads and writes through other descriptors, so other host processes neither see these locks nor are seen; a lock from the end of a directory throws. The return values and error codes are unverified on a console - fcntl on a guest socket (libkernel) answers
F_GETFD,F_SETFD,F_GETFLandF_SETFLand fails every other command withEINVAL; FreeBSD 11.4'skern_fcntlalso duplicates the descriptor (F_DUPFD,F_DUPFD_CLOEXEC) and handlesF_GETOWNandF_SETOWNon a socket.FD_CLOEXECis only recorded (there is noexec):dup,dup2and an accepted socket start without it,SOCK_CLOEXECandF_SETFDset it. None of this was measured on a console. - ioctl, _ioctl (libkernel) handle only
FIONBIOandFIONREADon a socket, and any other request there fails withENOTTY. On the other descriptors they follow FreeBSD'skern_ioctl,vn_ioctlandpipe_ioctl:FIONBIOsetsO_NONBLOCKasfcntldoes,FIOCLEXandFIONCLEXset and clear the close-on-exec flagF_GETFDreports, andFIONREADreports the size past the offset of a file, the queued bytes of a pipe's read end, 0 on its write end andENOTTYon the random device, while it throws on a directory or another character device. Every termios request (groupt,TIOCGWINSZincluded) fails withENOTTY, since those descriptors are taken as not a terminal, as intcgetattr, and any other request throws. Stremio Plus (PPSA74126) imports_ioctlfor its statically linkedtc*functions, which sendTIOCGETA,TIOCSETA,TIOCSPGRPand the other termios requests - Regular file mappings (libkernel) - on Linux
mmapmaps a regular file's descriptor shared or private like a shared memory object, with the same access checks; SQLite's WAL index (-shm) in Dusklight PPSA99305 needs it. FreeBSD 11mmap(2)and shadPS4'sMemoryManager::MapFilemap regular files; the console's behaviour is not measured. Other descriptors (pipes, sockets, directories, devices) still fail withEOPNOTSUPP, and Windows maps no files. - sendfile (libkernel) follows FreeBSD 11's
sendfilefor a connected stream socket: headers, the file fromoffset(to its end whennbytesis 0) and trailers, the file offset left unchanged,EAGAINwith*sbytesset after a partial send on a non-blocking socket. It reads the file throughsceKernelPreadin 64 KiB blocks and sends each withsend. A socket as the file fails withEINVAL, as on FreeBSD, but any other descriptorsceKernelPreadreads, such as/dev/urandom, is sent where FreeBSD requires a regular file or shared memory, and a file descriptor as the socket fails withEBADF, as forsend, where FreeBSD answersENOTSOCK.flagsare ignored: reads complete before sending, soSF_NODISKIOnever fails withEBUSY, andSF_SYNCreturns once the data is queued on the socket rather than sent. PS5MC (PPSA99420) imports it - pthread_kill (libkernel) returns the error number like FreeBSD's
pthread_kill:EINVALfor signals outside 0 to 128 andESRCHfor a null thread. Signal 0 reports any other thread handle as existing, without checking that the thread is still alive. A signal to the calling thread goes throughraise, so it sharesraise's limits; a signal to another thread is left on the pending set for whichever thread waits for or unblocks it, since it is not delivered to the chosen thread, and a handle that is not a live guest thread returnsESRCHwithout touching it - select (libkernel) validates
tv_usecbut saturates a valid timeout beyond the remainingsteady_clockrange attime_point::max(), so the wait can continue effectively indefinitely. The console's behavior for a valid timeout outside the host clock's range is unknown. - _fstatfs (libkernel) fills the FreeBSD 11
struct statfsof the PS5 payload SDK with the host volume that holds the file: block size, transfer size, total, free and available blocks, maximum name length and the read-only,nosuidandnoexecflags (on Windows only read-only, andf_filesandf_ffreeare 0, as NTFS has no fixed number of files). The filesystem type number and name, mount paths, owner, filesystem id and I/O counters are left 0 or empty, since they would describe a host mount, not a console one. Sockets and pipes returnEINVAL, as FreeBSD'sgetvnodedoes; a Windows descriptor that is neither a file nor a pipe throws. Stremio Plus (PPSA74126) reads the free space before a download and only logs the other fields - pthread_spin_lock (libkernel) - a lock taken again by the thread that holds it throws here. On a PS5 (firmware 12.70) that call had not returned after 2 s, as FreeBSD 11.4 libthr
thr_pspinlock.cspins forever - pthread_rwlockattr_getpshared, pthread_rwlockattr_setpshared (libkernel) -
pthread_rwlockattr_destroyclears the attribute here, so a later get, or a set of 0, throws. On a PS5 (firmware 12.70) destroy leaves the attribute set; a get afterwards returned 0 with a stale value and a set of 0 returned 0 - sceKernelGetEventError (libkernel) returns the event's
dataas anintwhenEV_ERROR(0x4000) is set inflagsand 0 otherwise, as KytyPS5 does and as FreeBSD 11's kevent reports a failed change (flags = EV_ERROR, errno indata). fpPS4 instead returnsfflagsplus0x80020000whenfflagsis nonzero. Whether the console readsdataorfflagsand returns the raw errno or an SCE error code is unverified. No event delivered by the equeue here carriesEV_ERROR, so the call returns 0 for every eventsceKernelWaitEqueuereports - pthread_cond_reltimedwait_np (libkernel) takes
(cond, mutex, usec), a relative timeout in microseconds, and isscePthreadCondTimedwaitwith POSIX error numbers. FreeBSD 11.4 has no such function; the signature comes only from shadPS4, which registers one function for this NID and forscePthreadCondTimedwait(condvar.cpp). shadPS4 reads a 64-bit count; here it is the 32-bitSceKernelUsecondsof the SCE twin, as forsem_reltimedwait_np. Unverified on the console - sysctl kern.arandom (libkernel) serves FreeBSD 11's MIB {1, 37} (CTL_KERN, KERN_ARANDOM) and the "kern.arandom" sysctlbyname lookup through std::random_device, returning min(*oldLength, 256) random bytes. Following FreeBSD's sysctl_kern_arnd, a null buffer reports min(*oldLength, 256) without writing, a null length returns 0 without writing, and requests with a new value return EPERM (1) as the node is read-only. Cryptographic quality depends on the host's random device implementation.
- pthread_atfork (libkernel) appends the three handlers to a process-wide list and returns 0, or
ENOMEMwhen the entry cannot be allocated, as FreeBSD 11.4's_pthread_atforkdoes; null handlers are accepted. The handlers never run:forkthrows andvforkis absent, so no child process is created. Handlers of a module that is unloaded stay in the list, where FreeBSD's__pthread_cxa_finalizeremoves them. PPSA97358 has a source-level call from the constructorrcu_init(rcu.c), but current-main runtime reachability is unverified; XPSemu currently stops first at the unresolvedmlockallimport (#4386) - sceKernelGetPageTableStats (libkernel) reports page-table entries over the recorded protection ranges (committed mappings; reservations are not recorded): each range charges one entry per 2 MiB granule it spans to the cpu pool when it has CPU access bits (0x7) and to the gpu pool when it has GPU access bits (0x30), so a range with both counts in both and two ranges sharing one granule charge it once each, as KytyPS5 sums per committed range. Mappings with no access bits, such as PROT_NONE or ACP-only protections, charge neither pool. The 2048-entry budget per side, its constants, and whether the console counts reservations or guest image segments are unverified. Resonance: A Plague Tale Legacy (PPSA30601) reaches this call after the other missing exports of its start-up are provided
- clock_settime (libkernel) validates like FreeBSD 11.4's
kern_clock_settimefor a root caller:EFAULTfor a null time,EINVALfor any clock butCLOCK_REALTIME, a negative second count or a nanosecond count outside 0 to 999999999. A validCLOCK_REALTIMErequest throws, naming the requested time, since the host clock is never set and no guest clock offset is kept. FreeBSD checksPRIV_CLOCK_SETTIMEfirst, which a jailed process fails withEPERM; whether a title holds it on the console is unverified. PS5MC (PPSA99420) imports it; no call site is known - chflags (libkernel) follows FreeBSD 11.4's
kern_chflagsatand UFS'sufs_setattrfor a root caller: the path lookup reportsEFAULT,ENOENTandENOTDIR, a flag outside the 17 UFS stores (VNOVALincluded)EOPNOTSUPP, andSF_SNAPSHOTEPERM. Neither host keeps BSD file flags, so every file is taken to have none (statreportsst_flags0): flags 0 succeed without touching the file, and any other flag throws, naming the flags.UF_READONLY,UF_HIDDEN,UF_SYSTEMandUF_ARCHIVEare FreeBSD's names for the Windows attributes but are not mapped to them: UFS stores them without enforcing them,FILE_ATTRIBUTE_READONLYalready carries the owner write bit ofchmodon Windows, and Linux has no equivalent a process can set withoutCAP_LINUX_IMMUTABLE. The console's filesystem types,EROFSon read-only mounts and the change-time update are not modelled - getpriority (libkernel) follows FreeBSD 11.4's
sys_getpriorityfor the only guest process, which is taken to run at nice 0:PRIO_PROCESSandPRIO_PGRPwith 0 or its own pid (the guest is taken to lead its own process group, seekill) andPRIO_USERwith 0 or its user id 0 return 0, any other id fails withESRCHand any otherwhichwithEINVAL. As in FreeBSD's libc, wheregetpriorityis the bare system call, errno is left unchanged on success. The nice value of a title process on the console is unverified, and the host nice of the process or of the threadsAPS5_THREAD_PRIORITY=highraises is not reported.setpriorityaccepts the same target ids (any other id fails withESRCH, any otherwhichwithEINVAL), clamps the value to FreeBSD'sPRIO_MIN-PRIO_MAX(-20 to 20) and stores it as the guest process's nice value, whichgetpriorityreturns; the host scheduling class is not changed, and whether a title process may lower the value without privilege is unverified - rtprio_thread, pthread_setconcurrency, pthread_getconcurrency (libkernel) -
rtprio_threadfollows FreeBSD 11.4'srtprio_thread(2). Every guest thread here runs underSCHED_FIFOorSCHED_RR(priority 256-767), whose rtprio class is real-time; the console's mapping from that priority to the rtpriopriorange (0-31) is unmeasured, soRTP_LOOKUPandRTP_SETthrow for such a thread rather than report an impossible priority. A thread that is not real-time reportsRTP_PRIO_NORMAL(3) with 0, andRTP_SETaccepts only that value. Another function fails withEINVAL, a nullrtpriowithEFAULT, a thread other than the calling one throws, and an id that is not a live guest thread withESRCH.pthread_setconcurrencystores 0 or more and returnsEINVALfor a negative level (the error itself, as FreeBSD's libthr does, with errno untouched);pthread_getconcurrencyreturns the level, which is a hint and changes nothing about the host's concurrency. Morrowind (mshivam019/morrowind-ps5) imports all three at start-up - sceKernelVirtualQuery returns VirtualQueryInfo with a one-byte flag allocation unit so Windows and SysV hosts return the same 72-byte representation, with the name at byte 33 and GPU mask at byte 65. This preserves the existing SysV representation; the console offsets beyond the address, protection and size fields observed in Astro Bot's AGC calls are not hardware-measured. Size and offset assertions plus a raw-byte named-mapping regression guard the host ABI.
- app0.zar files (libkernel) -
sceKernelOpen,open,sceKernelStatandsceKernelCheckReachabilityresolve a path missing fromapp0/inapp0.zar. An archived file is a read-only host null device whose reads, positioned and vectored reads, AIO reads, seeks,fstatandFIONREADare served from the archive, with the position shared bydup,dup2andF_DUPFD; writes fail withEBADFas on a read-only descriptor, opening one for writing or truncation fails withEROFSandO_CREAT | O_EXCLwithEEXIST.fstatandsceKernelStatreport mode0444(0555for a directory), the node index as inode and the archive file's device and times. Opening an archived directory or a directory present both on disk and in the archive throws, asgetdentswould miss entries. On an archived descriptormmap,flock, record locks,fstatfs,ftruncate,fsync,fdatasync,getdirentries,fchmodandfutimesthrow instead of acting on the null device;mmapisnoexcept, so its refusal terminates the process.lstatand the other path calls on archived files are not implemented - param.sfo (libkernel) is read when
param.jsonis absent, forTITLE,TITLE_ID,DOWNLOAD_DATA_SIZEandUSER_DEFINED_PARAM_1to_4, and mounts/dataand/download0likeparam.json; the localizedTITLE_00toTITLE_30are not selected by the console language.