WindowsTlsBuilder allocated the whole TLS block and thread control block
for .gtls, so a large p_memsz cost that much memory even though only the
p_filesz template is written. Store the template and reserve the rest as
the section's virtual size; the TLS directory still covers the whole
block, which the loader copies from the zeroed image.
PPSA15552 passes matching front/up after a listener move; pick a perpendicular up hint instead of throwing so Dead Cells can keep running.
Co-authored-by: Cursor <[email protected]>
Titles that import libSceNpTus failed to load because the library did
not exist. Title contexts and requests are tracked, arguments are
validated, and every storage operation completes as signed out, matching
how libSceNpManager reports PSN.
Since #7323 sceHmd2Initialize succeeds and sceHmd2Open returns a handle,
so PPSA01317 runs its PS VR2 set-up and aborts on
sceHmd2ReprojectionQueryBufferSizeAlign, which is not implemented
(reported in #6350). AnyPS5 has no PS VR2 or PC VR support, so
initialization returns libSceHmd's UNSUPPORTED_FEATURE (0x81110016)
again, as before #7323, and titles skip their VR set-up. sceHmd2Open
then returns NotInitialized. The reprojection size queries stay
unimplemented.
Refs #6350
RADV initializes and fast-clears DCC keys with its fill_memory compute
kernel, which stores through a global address. Run as an ordinary
dispatch, its stores name no range. The DCC key tracking therefore saw
a key fill only once it had landed in guest memory, which could be
after later writes to the surface had already been recorded.
vkQuake (PPSA99010) hits this on every item pickup:
- the scene renders into a second color buffer;
- RADV's UNDEFINED -> GENERAL barrier fills color buffer 0's keys with
DCC_CLEAR_0000;
- a screen-effects compute pass writes color buffer 0;
- the UI pass draws on color buffer 0.
The keys landed while the UI pass was being recorded. The refresh took
them for a clear made after the compute pass, dropped its results and
cleared the image to black. For the length of the pickup flash the
scene was black, with only the HUD drawn.
The fill HLE now matches the 16-byte variant of the kernel as a fill of
min(threads * 16, user[4] + 16) bytes. The kernel takes its address in
user[2..3], its largest store offset in user[4] and its value in
user[5]. A key fill through it is handled where it is in the command
stream.
In a payload process on a PS5 (firmware 12.70), the strtod and wcstod
of libSceLibcInternal.sprx keep the payload of a NAN(...) sequence,
read as FreeBSD 11's gdtoa hexnan reads it, under the quiet bit and
with the sign. strtof, wcstof, strtold and wcstold return the positive
default quiet NaN whatever the sign and the sequence, and consume a
sequence of letters, digits and underscores up to its closing
parenthesis. Here every NaN lost its payload, and strtof and strtold
kept the sign.
The model reproduces the 64 NaN inputs that p11, p14, p27 and p33
measured, including blanks and other characters inside the parentheses
and unterminated sequences, and the test now pins them. It also pins
two p71 cases measured through strtod: nan(1 2), and "nan( " followed
by NUL and ")". In hexnan, a blank after a payload digit, while a
32-bit word of the payload is still free, starts a blank skip that has
no NUL check, so when only blanks follow up to the terminating NUL the
console reads past it (p71); that case throws here. A blank right after
"nan(" is skipped with a NUL check, and the console stopped at the NUL
there.
AI-assisted: yes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
FlushPending moves the overlapping images from the pending list to the
flushing list and stores them under the GPU mutex. A FlushPending or a
flush hook on another thread for the same range found nothing pending and
returned while the store was still running, so the host read saw guest
memory before the store; the queue-idle store hits this with any host read.
When an overlapping image is being stored and this thread does not hold
the GPU mutex, take it and check again until no overlapping image is left
being stored, and report the range as stored so the hook syncs the
recorded store.
Fixes#7744
Every unverified point of the libSceSsl debt entry was measured on a PS5 on
system software 13.40 with a ps5-payload-dev payload against a TLS 1.2 test
server presenting leaves issued by the test fixture root. The code follows the
console where it differed: initial verify options 0x25 with the date checks
off, bits outside 0x3F rejected, the verify callback called with the context
id and the bitmask of failed checks and re-run from every later call after a
negative return, sceSslRead flags 0 reading until the requested length,
streams that end without close_notify reading as 0, resets reported with the
libSceNet codes, receive timeouts as SSL_ERROR_WANT_POLLIN, a fatal alert
ending the transport, sceSslClose as a repeatable no-op before the handshake,
read and write before connect running the handshake, SSL_ERROR_INVALID_ID for
unknown or deleted handles and terminated contexts, sceSslSetMinSslVersion
accepting 2 to 5, an empty ALPN list rejected, long attribute names from
sceSslGetNameEntryInfo and sceSslGetSerialNumber leaving the caller's length.
The debt entries record the measured values and what stays unmeasured.
A storage image refreshed over memory where other images still had results
pending stored those results first, then uploaded. When the image's DCC keys
are a clear code its upload is a GPU clear that reads none of that memory, so
those stores were dead for it. Unreal's render graph does this whenever it
hands transient memory to a new texture it fast-clears.
In write-watched memory the results now stay pending. Anything that reads the
memory before the image is written stores them as before. Once the image's own
results cover their 64 KiB units they are dropped, as its write-back stores
every byte of those units; an image written again since keeps its newer
results, and a unit stored after the hand-over no longer counts as a CPU store
against the image's write. The path is chosen from the keys read before any
flush, and only when no flush is needed. APS5_NO_CLEAR_HAND_OVER=1 stores the
results first as before.
While a trimmed import waits for the next refresh it pins no registered
range, so the guest can unmap its kept side and map new pages at the
same address without waiting for it. The new pages must be imported
again: the GPU has to read their bytes, not the old pages the trimmed
memory still pins.
agc_trimmed_import imports 32 MiB of singly mapped direct memory whole
and then cuts it: a 16 KiB page made inaccessible nearer its start or
nearer its end, an unmap of a 2 MiB resident run inside it, an unmap of
its first 2 MiB run, and an unmap of the first 1440 KiB of that run. It
checks that the larger side keeps the import's memory, or, when the
unmap replaced pages of the kept side's run, that it is imported again;
that the smaller side is imported apart from it; and that the GPU reads
the guest's bytes through each import while the write watch collects CPU
stores and reports none nobody made. Each unmapped span is then mapped
again at the same address, and the whole range must be served by a new
import that matches the new pages and the kept side.
The re-import bench only moved the start of the range. It now runs three
patterns, each on a fresh 1126 MiB range: the head or the tail loses GPU
access 1440 KiB at a time, or a 16 KiB page inside the kept side loses
all access (256 MiB in, then 1440 KiB above the one before). For every
cut it prints whether the kept side was trimmed or imported again, the
cut and import times and, for a cut inside, the import of the smaller
side. After each pattern it checks that the kept side still matches the
guest's bytes and write watch. The bench is registered on Windows only,
the one platform it runs on.
releaseTrimmed freed the trimmed import's memory at once when the
calling thread did not hold GpuMutex. It now hands the memory to the
active recorder whenever there is one, as retireImport does for the
imports importRunAt and refreshImports retire, so the memory outlives
the open batch and every batch submitted before it.
A protect or unmap strictly inside a plain host import still retired the
whole import, and the next access imported both sides of the cut again.
The larger side is now kept as a trimmed import, as a cut at the head or
the tail already is, and only the smaller side is imported again. Equal
sides keep the tail.
The summary line names the CPU, its thread count, physical memory and,
with param.json, the title ID, name and content version.
APS5_TRACE_BENCH=1 with APS5_TRACE_SUMMARY_EVERY rewrites
aps5-bench-<commit>-<pid>.json.gz at each interval with the time between
every two presents, so reports can be recomputed and checked from raw
frame times. param.json's contentVersion is read when present.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The over-limit row was a fixed 0xffffffff in X, which is a valid count
on devices that report that X limit (RX 9070 XT). Take the first axis
whose limit is below 0xffffffff and use limit + 1 there; skip when no
count can exceed the limits.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
recordDispatch now reads its indirect offset through HostImportOffset,
which adds the import's lead. The unchecked path passes that offset to
vkCmdDispatchIndirect; the clamp kernel reads from import.address plus
the guest offset, since import.address already includes the lead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Backing only the pages a pointer touches splits a zero-filled run into
one section per backed block. Each becomes a Mach-O segment, and dyld's
bind and rebase opcodes address at most 16 segments, so a module with
pointer slots on many separate zero-filled pages converted on main but
failed with --macos. When the segments would pass that limit, adjacent
sections with the same protection are merged again, smallest gap first.
Reported by jotitadev in the review.