10 Commits
Author SHA1 Message Date
Mihawk e98cca409c Measure Sony's local processes for this title's frontends
../PS5_Proton found that a title can have Sony's service start a fresh
executable as a child of its application (sceSystemServiceAddLocalProcess
with the libc-only preload mask, a socket at the child's descriptor 3,
sceSystemServiceKillLocalProcess). This probe asks what that is worth
here.

- frontends/child-probe: a frontend program (SDL2, PS5_OpenGL) that the
  title hands over to with LoadExec, as it does ES-DE. It holds the
  display while running a headless child, keeps a child alive across
  its own LoadExec, then releases the display and starts a graphics
  child. "nogpu" mode starts one without ever opening the display.
- frontends/child-probe-helper: the headless child, freestanding C with
  only libkernel, reporting its PID and the memory it sees.
- tooling/native: --preload-prx-flags writes the preload mask into the
  process parameters (0x50), as PS5_Proton's patch to the same tool
  does. Without it the output is unchanged.

Result on the base PS5 (13.40): a headless child starts in 0.12 s and
closes cleanly (kill 0, gone from the list). It reads the same flexible
memory figure as the parent (one budget), and it does not survive the
parent's LoadExec. A graphics child fails before its code runs
(gbase_gvmsw_open 6978, PRX_PROCESS_STARTUP_FAILURE), whether or not
the parent ever opened the display, and Sony then kills the whole
application.

Evidence: evidence/child-processes/ (capture.json, expectation.json,
probe-run-1.jsonl, probe-run-2-nogpu.jsonl). Command:
  PS5_FRONTENDS='es-de picker child-probe' tools/build-title.sh
  tools/run-title.sh --no-build --relaunch-test=1 \
    --relaunch-image=es-de/child-probe.bin --watch 120
Gates: tools/verify.sh PASS (format unit build integration evidence).
2026-10-04 18:36:36 -04:00
Mihawk 124099591c Run an SDL2/OpenGL frontend executable beside eboot.bin
EmulationStation is to be its own executable in the title, drawing with
../PS5_OpenGL (mihawk-99's fork of blackbearreloaded/ps5-opengl), whose Mesa
cannot share an image with RADV's. tools/build-frontend.sh builds a program in
frontends/ the way PS5_OpenGL builds its SDL2 consumers: its runtime shims,
heap and linker script over this project's base layout and C runtime, SDL2
from its bridge (built against this project's payload SDK from
ps5-payload-dev/SDL 8c56053), its OpenGL SDK and AGC import stubs, and a
256 MiB libc heap (the native tool's link takes --libc-heap-size; eboot.bin
keeps the largest). The result is es-de/<program>.bin, built by
tools/build-title.sh before the manifest for each program PS5_FRONTENDS names
(none by default). frontends/common supplies __memset_chk, which this host's
clang 23 builtins (emutls) call.

tools/deploy-title.py verifies a signed executable other than eboot.bin by its
stored bytes, on a connection with ftpsrv's SELF transfer mode off; it used to
compare the decoded download and upload twice. tools/run-title.sh's
--relaunch-image uses a built image at that path rather than a copy.

frontends/sdl-probe opens an SDL window with an OpenGL 3.3 core context,
shows red, green and blue for two seconds each, reads the centre back, counts
controller events, records /app0/es-de/sdl-probe.json and restarts eboot.bin
with the argument it was given.

Verified:
  TMPDIR=build/tmp bash tools/verify.sh -> PASS (format unit build
    integration evidence), default title unchanged
  PS5_FRONTENDS=sdl-probe tools/build-title.sh -> es-de/sdl-probe.bin
    (23,401,527 bytes) in the manifest, check-manifest PASS, build c1383963
  tools/run-title.sh --no-build --relaunch-test=1
    --relaunch-image=es-de/sdl-probe.bin --watch 45 on the base PS5: kernel
    log EXEC eboot.bin, es-de/sdl-probe.bin, eboot.bin; GL "4.6 (Core
    Profile) Mesa 26.2.0" on "PS5 AGC", 1920x1080, 121/120/120 frames in two
    seconds each with every centre pixel right, a joystick and a button
    press; relaunch PASS; the owner saw red, green, blue, then RetroArch
  deploy of the probe alone after the fix: one upload, verified as stored
Evidence: evidence/sdl-probe/ (python3 tools/evidence.py compare)
2026-10-04 15:05:48 -04:00
Mihawk f1b9a533b9 RPCS3: a game's folder loads; the title's getcwd from the platform
Loading God of War HD's folder with "Use this directory" crashed the title:
canonicalising a relative path asks libc++ for the working directory, and the
title's own getcwd resolves to nothing (libc's calls __getcwd, which only
libkernel_sys has). The title now links it to ps5_getcwd, as the cores were,
and app-symbols.map keeps every __wrap_ symbol local: lld exported
__wrap_getcwd to interpose on libc's stubs, which the native converter
refuses. The fork (its folder loading) boots the game in a folder rather than
RPCS3's cache build of it.
2026-09-29 06:38:52 -04:00
Mihawk 8a457a133f Seven more cores: PS1, N64, Saturn, C64, MAME, DS and 3DS
Beetle PSX HW, Mupen64Plus-Next, Beetle Saturn, VICE x64sc, MAME 0.289,
DeSmuME and Azahar, each built from my fork of it at a pinned revision
(tools/core-fork.sh, tools/build-<core>.sh), with the PS5 defaults at the
most each offers that holds full speed.

The loader grew to fit them. A core's file and image come from direct memory,
up to 1 GiB (MAME's image is 413 MB), and each image is zeroed when it is
reserved: direct memory is not, and a reloaded core's .bss held its
predecessor's bytes, which faulted every core on a reload. Each image's
.eh_frame is registered with the title's unwinder, for the cores that throw.
Initializers are capped at 65,536 (Azahar has 2,476). A load that misses imports
lists all of them. Aligned operator new comes from the core heap, a core's
atexit belongs to the core, and the imports the new cores need are bound to the
platform layer's getcwd, tmpfile, mkstemp and the rest (SDK fork ebd0fe2).
2026-09-28 15:06:47 -04:00
Mihawk 9155dd5c23 Add native Snes9x with verified colours and core-local C++ cleanup
Build pinned libretro Snes9x 1.63 with this project's SDK and official metadata.
Convert final RGB565 frames to independent XRGB8888 storage for the existing
Vulkan upload path. Integrate staging, native imports and loader diagnostics.

Keep C++ destructor registration inside the unloadable core. Validate bounded
fini arrays before constructors and execute cleanup at last-reference close.
Test all 65536 colours through GPU upload, pitches/bounds/cached frames and
C++ destruction/reload/malformed finalizers. Existing core bytes are unchanged.

Verification: bash tools/verify.sh passed format/unit/build/integration/evidence
with 60 tests. bash tools/run-title.sh --no-build --core-test=snes9x --watch 180
passed loader/recovery and remained running until runner close. Owner: Works
flawlessly, answering colours/audio/input and menu/content-transition checks.
Zero Vulkan refusals, GPU API failures or kernel fatal-signal reports.

Evidence: evidence/snes9x-native/{capture,expectation,iterations,abi,build}.json,
loader/recovery reports and exact linked-driver hashes. Replay with
bash tools/verify.sh evidence. Frontend identity:
64bef2e7826e5a63a6644a933e24ef5403d28c754230c0038eb8a76b4baa1c53.
PS5_Vulkan was not modified; special-mode/save-state/performance coverage is
not implied by this ordinary-game acceptance.
2026-09-19 16:19:59 -04:00
Mihawk 63a440414a Load native FCEUmm cores safely and present software core frames
Replace payload dlfcn hooks with a bounded ELF loader and explicit native
runtime bindings. Separate core RX/R/RW segments, reject unsupported ELF
features, and release failed loads. Guard failed menu selection before
frontend teardown and stop polling drivers after failed initialization.

Read core files through mapped buffers and bounded POSIX reads. Convert
libretro XRGB8888 frames to matching RGBA8 staging/sampled textures so real
content uses the verified Vulkan copy path rather than the RGB565 compute
branch. Keep video_ps5 selectable and preserve frontend/audio/error logs.

Validation: tools/verify.sh passed all five gates (52 tests). Console command:
tools/run-title.sh --no-build --core-test --watch 180, with manual core/game
selection. Eight ELF load/unload cycles and live-menu failure recovery passed;
owner confirmed flawless gameplay. Zero GPU refusals/API failure records and
kernel fatal signals across captured launches. Return-to-menu shortcut and
repeated game unload/reload remain unverified; owner closed/reopened the app.

Evidence: evidence/native-core-loading/{capture,expectation,loader,recovery,
abi,iterations}.json; replay with tools/verify.sh evidence. Accepted title
identity 9b287ca7e0d4a04721c68d30fb88b678a62cc568c1f2d44b75716b464c21b6d5.
No PS5_Vulkan edits by this task; its independently advancing checkout is
not used as a substitute for the accepted title's archive-bound identity.
2026-09-19 14:47:09 -04:00
Mihawk 34d9200ecf Get the Vulkan driver to a created pipeline, a running runloop and a first frame
The console run now reaches a frame. In order: video_vulkan selected, the console's
khr_display WSI, a device, a swapchain at 3840x2160, four textures, a vertex buffer,
the stock shader reflected, both stages compiled by the driver's own compiler,
vkCreateGraphicsPipelines returning 0, vulkan_init handing back a live pointer, the
input driver opening the pad, and runloop_iterate reaching vulkan_frame -> vkQueueSubmit.
Nothing is on screen yet: the first frame's command buffer already has a render pass
open when the backbuffer pass begins, and Mesa's common CmdBeginRenderPass2 asserts.

Three of the four fixes are port patches, all of them requests that driver refuses:

  0016  its pipeline check accepts only VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, and its
        draw path refuses a non-indexed draw whose first vertex is not zero; the two
        quads are now six-vertex lists, the second triangle reached through the
        binding's own offset. This is what turned VK_ERROR_UNKNOWN into a pipeline.
  0017  it refuses any sampler that is not clamp-to-edge and leaves the output handle
        untouched, while CommonResources destroys every handle that is not
        VK_NULL_HANDLE - sixteen refused samplers were destroyed as if real and the
        driver asserted. The array is cleared before it is filled.
  0018  the khr_display context reported quit from the frontend's signal-handler
        state, so the runloop ended on its first iteration and rarch_main returned 0
        with no frame drawn. A console title has no terminal and no SIGTERM sender.

The fourth is this port's own CRT: _start went from _init_env straight to the static
constructors and never cleared the BSS, whose tail is 0xc7040 bytes of a segment that
is 0x104b4 bytes in the file. tooling/native/ps5-pie.ld marks __bss_start/__bss_end
and _start clears that range; every run prints the check that says so. Whether the
loader also zeroed it was never measured - the invariant is what this asserts.

Two instruments, both because nothing else could be read:

  src/main.cpp sends stderr to /app0/trace.txt and makes it unbuffered. The driver's
  __assert prints the expression, file and line to stderr and then aborts, and the
  console's libc buffers that stream, so every assertion arrived as a bare `abort is
  called(system)`. The trace now carries the assert text, which is what identified the
  render-pass state as the current blocker.

  tools/apply-runtime-probes.py carries thirty-nine probes, and tests/test_frontend.py
  checks the set itself: an insert that ends with its own anchor duplicates the line it
  is inserted before, which broke the build three times in this round. Every probe must
  carry a marker in its own text, a note and a trailing newline.

Evidence: bash tools/run-title.sh with the probe set applied (captures under klog/,
probes reverted before this commit); the trace's own lines quoted above;
bash tools/verify.sh PASS with a probe-free image (`strings build/llvm-pie.elf |
grep -c 'probe PIPE2|probe VDRV|...' ` = 0 after deleting build/ra/obj, which is what
a revert needs); 20 unit tests including the probe-set checks.

Next: mark the span between the offscreen-passes call and the backbuffer pass - the
first-frame history/feedback clear and the menu texture upload - and fix whichever of
the two leaves a render pass open.
2026-09-19 01:03:05 -04:00
Mihawk be552f1460 Link the Vulkan driver into the title, and resolve the 24 symbols that stopped it
../PS5_Vulkan's driver stops being a library RetroArch loads at run time and
becomes ordinary symbols in eboot.bin, because a PS5 title cannot dlopen one
(9d29a1f measured ENOEXEC, ENOENT, NULL and ESRCH). This is the commit where
that link completes.

The first link failed on 24 undefined symbols, which is three problems rather
than one. The count is only knowable because the link line now carries
--error-limit=0; before that lld stopped at twenty.

 - Four __eh_frame_* boundaries. The driver's shader compiler is C++ and links
   the SDK's libunwind, which finds unwind tables through these boundary symbols
   rather than dl_iterate_phdr. tooling/native/ps5-pie.ld provides the same four
   ../PS5_Vulkan/tooling/psbc/ps5-pie-unwind.ld defines, in this project's own
   copy of the layout.
 - Eight Mesa utility symbols (u_thread_create, u_thread_setname,
   util_barrier_init/destroy/wait, util_thread_get_time_nano,
   os_create_anonymous_file, os_read_file). That project's PS5 object list
   filters src/util/{u_thread,anon_file,os_file}.c out of the compiler archive,
   and its own libvulkan.so.1 links only because a shared object may leave
   symbols undefined. tools/build-mesa-util.sh compiles Mesa's own definitions
   with that project's PS5 configuration, because u_thread.c's util_barrier
   layout has to match the u_queue.ps5.o already inside the archive.
 - Twelve sceAgc* entry points. The console provides libSceAgc and
   libSceAgcDriver and the SDK stubs neither, so they are declared for the
   converter to record as imports. They live in tooling/ps5-stubs/ rather than
   vendor/, which is not in the diff.

Evidence:

  bash tools/build-title.sh   exit 0, no undefined symbols; the artifact grew
                              from 8,225,706 to 29,859,722 bytes, which is the
                              driver and its shader compiler
  bash tools/verify.sh        PASS (format unit build integration evidence)
  unittest discover -s tests  18 tests, two new: every sceAgc* the three driver
                              archives reference is declared by the stubs (20
                              referenced, 21 declared, none missing), and the
                              linked build/llvm-pie.elf defines the four
                              __eh_frame_* symbols

Not proven: the driver has not been on the console since it was linked.
2026-09-18 22:50:12 -04:00
Mihawk 6b54bbe862 Link the title and register the PS5 video driver
The frontend now builds into a signed title, and the PS5 driver is in
RetroArch's table in the image that ships, not only in the source.

  bash tools/build-title.sh
    -> 225 of 225 sources compiled, archived into build/ra/libretroarch.a
    -> dist/PPSA99169/eboot.bin, 8,026,239 bytes
       container: signed, plaintext; 12 segments; integrity: valid
       digest 71c88975040535a02b462618dd394a7a378034272c9bffa43ce0ca4b717ab9b7
       (identical across two consecutive builds)

  readelf -r build/ra/obj/gfx_video_driver.c.o
    -> .rela.data.video_drivers holds exactly two entries: video_ps5, video_null
  nm build/llvm-pie.elf
    -> video_ps5, video_null, rarch_main and a single main

  bash tools/check-manifest.sh
    -> 7 files match their digests, none extra, eboot.bin magic 4f153d1d, and it
       fails as it should when one byte of the image is changed

  tools/verify.sh
    -> PASS (format unit build integration evidence)

Registration and entry point. The port's changes are applied by
tools/apply-port-patches.py to the configured copy under build/ra-conf and never
to vendor/retroarch; patches/series names both. RetroArch's own main is compiled
out with -DHAVE_MAIN, the flag its desktop build passes, so src/main.cpp supplies
the only one and calls rarch_main.

Five faults fixed on the way, each recorded in docs/PHASE_LOG.md: the build read
sources from vendor/ and headers from build/ra-conf, so the driver table in the
image had no ps5 entry; 36 hand-written -DHAVE_* flags included six features
configure had turned off; config.h alone is not enough because a source can test a
feature before it is reached (libchdr without zlib); five Linux-only subsystems
were in the object list and are now off at their own configure switches; and two
link-time stubs the pipeline refuses needed -DNDEBUG and -DZSTD_TRACE=0.

The abandoned route is removed: deploy.py, console-launch.sh, check-payload.sh,
scaffold-native.sh, build-native-app.sh, install-title.sh and deploy.sh. Every
tool named by another tool now exists - tools/fetch-retroarch.sh was named by
three tools and three documents and had never been committed, and it now pins
RetroArch at v1.22.2 (69a4f0e) and drops the history so upstream cannot be
committed into. The gate scripts that had never been written are written:
lint-shell, lint-format, check-manifest. The template's unit test, which built a
test for a file that does not exist here, is replaced by tests/test_frontend.py.

The title has not run yet. It is built, signed and manifested; the folder has to
reach /data/homebrew/PPSA99169, and writes from this machine do not take on that
console.
2026-09-18 14:37:57 -04:00
Mihawk 4df5c68c11 Scaffold the native pipeline for RetroArch, with the display layer
Sets up this project as the native pipeline's scaffolding plus RetroArch's
sources, so RetroArch is compiled for the title pipeline rather than converted
into it.

- tools/scaffold-native.sh builds the scaffold from
  ../ps5-native-app-boilerplate-main: tooling, runtime (digest verified),
  Makefile and its tools, merged into this repository's tools/ rather than
  replacing them. An earlier version replaced tools/ wholesale and deleted this
  project's own scripts; the copy is now additive and the reason is in the file.
- tools/fetch-retroarch.sh clones the pinned upstream tag into vendor/ and
  records the revision, so "built from RetroArch 1.22.2" is checkable.
- src/display.{hpp,cpp} owns the console's display: VideoOut, direct memory, two
  registered buffers, present-and-wait. The sequence and its two load-bearing
  details come from the sibling application that proved it on hardware.
- src/main.cpp draws a moving bar: the cheapest thing that proves build, title
  format, launcher, VideoOut and the direct-memory path together.
- sce_sys/param.json is this project's identity, PPSA99169.

Evidence: src/display.cpp compiles through the pipeline's compiler
(tooling/prospero-clang18 with PS5_CLANG=/usr/bin/clang, -std=c++20), and a
RetroArch source file compiles the same way with its include paths.
2026-09-18 13:50:22 -04:00