Skip to content

feat(libSceVrTracker2): validate initialization and pass through the Hmd2 tracking result - #10566

Merged
oneandonlydean merged 4 commits into
boykopovar:mainfrom
hypermoist:feat/vrtracker2-initialize
Oct 25, 2026
Merged

oneandonlydean merged 4 commits into
boykopovar:mainfrom
hypermoist:feat/vrtracker2-initialize

Conversation

@hypermoist

@hypermoist hypermoist commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

What

Implements sceVrTracker2Initialize in place of its NotImplemented stub. The behaviour is not measured on a console.

  • Validation, in the module's order, all 0x8A740006: null record or size other than 72; mode other than 0, 1000 or 1001; null work memory; work memory below 32 MiB (mode 0), 7 MiB (1000) or 3 MiB (1001); alignment below 0x10000 (only a minimum, not a power of two); any reserved byte at 0x3a..0x47 set; either thread priority outside 256..767; affinity masks outside bits 0..12 (mode 0) or outside 0xEA00 (1000/1001). Then the module state moves from 0 to 1, else 0x8A740002.
  • After that every failure undoes what the call opened and puts the state back to 0. Mode 0 clears the first 32 MiB of work memory first. The console's application attribute checks on the sceKernelGetAppInfo2 record (0x8A74001E, 0x8A740020) are skipped: the host has no such record, and a zeroed one passes them. The signed option at 0x39 accepts -1 and 0, and 1 only for titles compiled with SDK 7.00 or later.
  • Modes 0 and 1000 then call libSceHmd2 TrackingOpen, TrackingStart, LedDetectorOpen and LedDetectorStart and return the first non-zero result as is. With no headset main's libSceHmd2 answers 0 and -1 (its measured pre-initialization answers, docs/dev/debt/libSceHmd2.md), so Initialize returns -1, the module stays uninitialized and every other export keeps answering 0x8A740001.
  • Still throws: modes 1000/1001, which need the application category the host doesn't have, and a fully successful Hmd2 sequence, which would need the tracker runtime. The daemon IPC client and the work-memory heap aren't created on the host; neither is visible from the caller's side on the failing path.
  • The eight tracking and LED-detector exports in libSceHmd2 become named functions (same NIDs, same answers) so libSceVrTracker2 can import them by name across the NID patcher.
  • The debt entry is cut to what stays open.

Tested

Linux (CachyOS, GCC, Release): ctest --test-dir build -R 'guest_vr_tracker2|guest_hmd2$' --output-on-failure 2/2 passed.
Windows (MinGW-w64 GCC 15.2.0 winlibs cross build, run under Wine): ctest --test-dir build-win15 -R 'guest_vr_tracker2|guest_hmd2$' --timeout 120 --output-on-failure 2/2 passed. The NID-patched libSceVrTracker2.prx imports B0b8DeIUGes, 1oPZizn0SNA and the other six from libSceHmd2.prx, which exports them, on both platforms.
guest_vr_tracker2 covers each validation error in order, the -1 pass-through, the 32 MiB clearing in mode 0 (and that a validation failure writes nothing), a retry after the failure, sceVrTracker2Finalize and an uninitialized export still answering 0x8A740001, the late coordinate-system check coming after the Hmd2 error, and the 0x39 option against SDK 6.xx and 7.00.

Checklist

Assisted With AI: Yes. All tests were run locally on computer hardware (AMD Ryzen 5 5600G, 32 GB RAM, NVIDIA GeForce RTX 3060, CachyOS Linux 7.3, GCC 16.2), not on an actual console.

…Hmd2 tracking result

sceVrTracker2Initialize follows the order read from firmware 13.60
libSceVrTracker2.sprx (disassembly, not measured on a console). It
rejects with 0x8A740006, in order: a null record or a size other than
72; a mode other than 0, 1000 or 1001; a null work memory; a size below
the mode's 32 MiB, 7 MiB or 3 MiB; an alignment below 0x10000; a
non-zero reserved byte at 0x3a..0x47; a thread priority outside
256..767; an affinity mask outside bits 0..12 in mode 0 or outside
0xEA00 in modes 1000 and 1001. It then moves the module state from 0 to
1 and returns 0x8A740002 if it was not 0.

From there every failure undoes what the call opened and resets the
state to 0. Mode 0 clears the first 32 MiB of work memory first. The
application attribute checks on the sceKernelGetAppInfo2 record
(0x8A74001E, 0x8A740020) are skipped: the host has none, and a zeroed
record passes them. The signed option at 0x39 accepts -1 and 0,
and 1 only for titles compiled with SDK 7.00 or later. Modes 0 and 1000
then call libSceHmd2 TrackingOpen, TrackingStart, LedDetectorOpen and
LedDetectorStart and return the first non-zero result unchanged.
Without a headset main's libSceHmd2 answers 0 and -1 (its measured
pre-initialization answers), so Initialize returns -1 and every other
export keeps answering 0x8A740001.

Modes 1000 and 1001 need the application category, which the host does
not have, and a fully successful Hmd2 sequence would need the tracker
runtime; both throw. The eight tracking and LED-detector exports become
named functions in libSceHmd2 so that libSceVrTracker2 can import them
by name; their answers are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the unblocks title Names a title ID on the Unblocks line label Oct 24, 2026
@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Progress report (811ffe1 → 83043ee)

📉 System libraries: 49.26% (-0.04%, +8 declared functions)

🆕 8 declared as stubs
Library Name
libSceHmd2 sceHmd2InternalLedDetectorClose
libSceHmd2 sceHmd2InternalLedDetectorOpen
libSceHmd2 sceHmd2InternalLedDetectorStart
libSceHmd2 sceHmd2InternalLedDetectorStop
libSceHmd2 sceHmd2InternalTrackingClose
libSceHmd2 sceHmd2InternalTrackingOpen
libSceHmd2 sceHmd2InternalTrackingStart
libSceHmd2 sceHmd2InternalTrackingStop

Changed libraries (implemented exports)

Library Before After Change
libSceHmd2 13/126 (10.32%) 13/134 (9.70%) -0.62 pp

@MarcZX10

Copy link
Copy Markdown
Contributor

One test coverage gap: WorkMemoryCleared checks a 4 KiB window centered at 16 MiB plus the tail after 32 MiB. That shows the clear reaches the middle, but a 16 MiB clear could still pass, and the first bytes of the cleared range and the byte immediately before the boundary are not checked. Could you assert the start and last byte of the 32 MiB range plus the first tail byte, or scan the whole expected range? The production memset length looks correct; this would make the reported 32 MiB boundary coverage precise.

Scan every byte of the 32 MiB tracker range and the tail after it, so a
shorter clear or one that stops before the boundary fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hypermoist

Copy link
Copy Markdown
Contributor Author

@MarcZX10 good catch. Done in 16c9c31: WorkMemoryCleared now checks every byte of the 32 MiB range for 0 and every byte of the tail after it for the 0xAA fill, so the start, the byte before the boundary and the first tail byte are all covered. As a check, clearing half the size or one byte short of 32 MiB now makes guest_vr_tracker2 fail; the previous 4 KiB window passed both. With the production memset unchanged it passes on Linux and under Wine (WinLibs GCC 15.2.0), and the patched libSceVrTracker2.prx builds.

Assisted With AI: Yes But all test done locally on actual hardware

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The follow-up closes the coverage gap I raised: WorkMemoryCleared now checks every byte of the 32 MiB clear region and the entire sentinel tail, so the start, end, and boundary are covered. I reviewed the current implementation and test diff, including ordered validation, the state transition and rollback on Hmd2 failures, retry behavior, and the documented host-only limitation. The author reports the focused vr|hmd suite passing 8/8 on Linux and under Wine/MinGW; the latest hosted eligibility, conventions, labels, and report checks pass, while platform build jobs were skipped by the eligibility gate. I did not rerun those test suites locally.

@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 7658 core/libs/prx/libSceHmd2/Export.cpp
PR 8645 core/libs/prx/libSceHmd2/Export.cpp

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head refs/review/pr-10566 ($(git rev-parse refs/review/pr-10566)). The recovered parameter checks precede the state transition and work-memory clearing; initialization claims the state atomically, tracks successful Hmd2 open/start operations, rolls them back in reverse order, and restores the uninitialized state before returning the downstream result or throwing for the still-unsupported system modes. The tests cover validation boundaries, memory clearing, retry after failed start, and SDK-gated option mode. git diff --check is clean; hosted eligibility, conventions, labels, and report pass. All hosted platform build jobs are skipped, so this approval relies on source review and the contributor’s reported local tests; no local build was run.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 24, 2026
…tialize

Resolved Export.cpp and GuestVrTracker2.cpp keeping main's header layout; the new sceHmd2Internal exports are declared in the libSceHmd2 header and the test uses the library headers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 25, 2026
@oneandonlydean oneandonlydean self-assigned this Oct 25, 2026
@oneandonlydean

Copy link
Copy Markdown
Collaborator

@hypermoist Please drop the new system-mode throw cases in GuestVrTracker2.cpp:96-101; those modes intentionally remain unimplemented and the test should not pin that limitation. Keep the invalid-argument, full memory-clear/tail, SDK-option and failure/retry checks. EnterApplicationRoot also removes a predictable PID-based temporary path before creating it, so PID reuse can delete an earlier run's files. Please allocate a fresh directory atomically and clean up only the directory this run created, preserving pre-existing paths. Then rerun guest_vr_tracker2 and guest_hmd2 on Linux and Windows.

Remove the system-mode throw cases in GuestVrTracker2.cpp that tested
modes 1000 and 1001 throwing. Allocate a fresh temporary directory
atomically in EnterApplicationRoot without clobbering existing paths.
Update the debt entry without citing a source.

Assisted-by: Antigravity:gemini-3.8-flash-medium
@hypermoist

Copy link
Copy Markdown
Contributor Author

@oneandonlydean thanks, addressed in 83043ee:

  • Dropped the system-mode throw cases in GuestVrTracker2.cpp that tested modes 1000 and 1001 throwing. All invalid-argument, memory clearing, SDK option, failure and retry checks remain.
  • EnterApplicationRoot now allocates a unique temporary directory atomically using a random suffix without removing any pre-existing directory, and cleans up only the directory created for the test run.
  • Rebuilt and passed guest_vr_tracker2 and guest_hmd2 on Linux and under Wine (WinLibs GCC 15.2.0 cross-build). check_conventions.py is clean.

Assisted With AI: Yes But all test done locally on actual hardware

@oneandonlydean
oneandonlydean merged commit ebcde52 into boykopovar:main Oct 25, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build docs prx unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants