#!/usr/bin/env python3 # SPDX-License-Identifier: GPL-3.0-or-later """Validate the offline Phase-1.0P VideoOut submit analysis.""" from __future__ import annotations import argparse import json from pathlib import Path import subprocess from typing import Any PHASE = "PHASE_1_0P_VIDEOOUT_SUBMIT_OFFLINE_ANALYSIS" STATUS = "VIDEOOUT_SUBMIT_FAILURE_SITE_PROVEN_ROOT_CAUSE_UNRESOLVED" ARTIFACT_SHA256 = "c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd" TRACE_SHA256 = "3d0b8811ae11f5cac2c2d331e252e1789a10588cab0e6045828a6b6af0fe1eb6" RA_SOURCE = "12cf1d783c41eb303987e49a5a920805a59ef7a4" RA_RUNNER = "606909706f91d7213751c245081333f56c2cce89" SDK_COMMIT = "d2e2e585740362976a39fdd5ccf390f199a7bc37" SDL_COMMIT = "0baf4ac49382b537ba449901b5b6d0d189bb1fbb" AUTHORIZATION_FIELDS = ( "ps5_connection_authorized", "device_transfer_authorized", "device_execution_authorized", "result_receive_authorized", "installation_authorized", "autoload_authorized", "device_write_authorized", "automatic_retry", ) def load_json(path: Path) -> dict[str, Any]: value = json.loads(path.read_text(encoding="utf-8")) if not isinstance(value, dict): raise ValueError("analysis manifest is not an object") return value def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: return all(record.get(field) is False for field in fields) def bound_inputs_are_exact(record: dict[str, Any]) -> bool: return ( record.get("artifact_name") == "retroarch_ps5_write_diag.elf" and record.get("artifact_size") == 1845208 and record.get("artifact_sha256") == ARTIFACT_SHA256 and record.get("trace_size") == 18031 and record.get("trace_sha256") == TRACE_SHA256 and record.get("linked_sdl_source_size") == 19642 and record.get("linked_sdl_source_sha256") == "9949a280fed40241746788a8c001280455bc629e0566a88f82f16a8634d43025" and record.get("linker_map_size") == 637728 and record.get("linker_map_sha256") == "6768ffc7267b9b362c3b953571e5dfeed1e70004a3e19f41ef500c3f26272719" ) def exact_call_is_bounded(record: dict[str, Any]) -> bool: return ( record.get("symbol") == "sceVideoOutSubmitFlip" and record.get("tuple") == "(handle,0,1,0)" and record.get("function_address") == "0xfe560" and record.get("call_address") == "0xfe7bc" and record.get("got_address") == "0x1779b0" and record.get("registers") == {"edi": "handle", "esi": 0, "edx": 1, "ecx": 0} and record.get("runtime_handle") == "0x4e100100" and record.get("runtime_return") == -1 and record.get("saved_errno") == 0 and record.get("wait_called_after_failure") is False ) def abi_claims_fail_closed(record: dict[str, Any]) -> bool: required_unproven = ( "flip_mode_semantics", "frame_id_semantics", "buffer_zero_validity", "buffer_attribute_semantics", "flip_master_or_active_app_requirement", "return_code_semantics", ) return ( record.get("sdk_exports_symbols_only") is True and record.get("sdk_public_videoout_headers_present") is False and record.get("sdl_fork_is_only_local_public_prototype_source") is True and record.get("submit_signature_independently_corroborated") is False and all(record.get(field) == "UNPROVEN" for field in required_unproven) and record.get("errno_is_useful_for_observed_failure") is False ) def terminal_order_is_not_promoted(record: dict[str, Any]) -> bool: return ( record.get("observed") == ["D07", "D12", "D04"] and record.get("source_deterministic") is True and record.get("network_reordering_required") is False and record.get("d12_meaning") == "FAILURE_SHUTDOWN_REQUEST" and record.get("d12_proves_cleanup_complete") is False and record.get("current_trace_valid_terminal_classification") is False and record.get("host_parser_relaxation_allowed") is False and record.get("future_protocol_requires_new_magic") is True and record.get("future_terminal_after_d04_and_cleanup_evidence") is True ) def decision_is_fail_closed(record: dict[str, Any]) -> bool: return ( record.get("classification") == STATUS and record.get("parameter_experiment_allowed") is False and record.get("new_videoout_export_call_allowed") is False and record.get("host_parser_promotion_allowed") is False and record.get("phase10q_target_build_allowed") is False and record.get("phase10q_device_action_allowed") is False and record.get("required_next_evidence") == "ACCEPTABLE_PUBLIC_VIDEOOUT_ABI_AND_RUNTIME_STATE_CONTRACT" ) def git(root: Path, *args: str) -> str: result = subprocess.run( ["git", *args], cwd=root, capture_output=True, text=True, check=False) if result.returncode: raise RuntimeError(result.stderr.strip() or "git failed") return result.stdout def validate_repository(root: Path, retroarch_root: Path, sdk_root: Path, sdl_root: Path) -> list[str]: errors: list[str] = [] record = load_json( root / "manifests/retroarch/phase-1.0p-videoout-submit-analysis.json") if record.get("phase") != PHASE or record.get("status") != STATUS: errors.append("phase/status mismatch") commits = record.get("source_commits", {}) expected = { "chimera_gfx_phase10o": "af283ef93fae907e6c9c376df24d053ada3f7318", "chimera_retroarch_artifact": RA_SOURCE, "chimera_retroarch_runner": RA_RUNNER, "ps5_payload_sdk_v0_41": SDK_COMMIT, "public_ps5_sdl": SDL_COMMIT, } if commits != expected: errors.append("source commit binding mismatch") if not bound_inputs_are_exact(record.get("bound_inputs", {})): errors.append("artifact, trace, source or map binding mismatch") actions = record.get("offline_actions", {}) if not all(actions.get(field) is True for field in ( "source_inspected", "linker_map_inspected", "disassembly_inspected", "dynamic_symbols_and_relocations_inspected", )) or not all(actions.get(field) is False for field in ( "target_source_changed", "target_artifact_created", "ps5_connected", "device_transfer_performed", "target_execution_performed", "result_received_from_device", )): errors.append("offline-only action boundary mismatch") if not all_false(record.get("current_authorizations", {}), AUTHORIZATION_FIELDS): errors.append("authorization remains active") if not exact_call_is_bounded(record.get("exact_call", {})): errors.append("exact submit call is not bounded") registration = record.get("registration", {}) if not ( registration.get("function_address") == "0xfdc20" and registration.get("runtime_return") == 0 and registration.get("buffer_index_used") == 0 and registration.get("opaque_layout_semantics") == "UNPROVEN" ): errors.append("registration evidence was broadened") if not abi_claims_fail_closed(record.get("abi_evidence", {})): errors.append("ABI uncertainty was promoted") candidates = record.get("candidate_matrix", {}) if not ( candidates.get("diagnostic_normal_first_index_mismatch") == "REJECTED_CURRENT_CAUSE" and candidates.get("buffer_registration_failure") == "REJECTED_BY_OBSERVED_RETURN_ZERO" and candidates.get("errno_specific_failure_identity") == "ABSENT" and all(candidates.get(field) == "UNPROVEN" for field in ( "wrong_submit_tuple", "wrong_opaque_buffer_contract", "missing_flip_master_or_app_state", "visible_flip", "complete_cleanup", "safe_exit", )) ): errors.append("candidate matrix overclaims a root cause") if not terminal_order_is_not_promoted(record.get("terminal_ordering", {})): errors.append("terminal ordering is promoted or incomplete") if not decision_is_fail_closed(record.get("decision", {})): errors.append("decision is not fail-closed") tests = record.get("tests", {}) if not ( tests.get("chimera_gfx_ctest") == "60_OF_60_PASS" and tests.get("phase10p_guardrails") == 20 and tests.get("hardware_claim_from_host_test") is False ): errors.append("test evidence mismatch") tracked = git(root, "ls-files").splitlines() if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg", ".map")) for path in tracked): errors.append("target artifact or linker map is tracked") try: if git(retroarch_root, "rev-parse", "HEAD").strip() != RA_RUNNER: errors.append("RetroArch HEAD mismatch") patch = git(retroarch_root, "show", f"{RA_SOURCE}:pkg/ps5/sdl2-ps5-early-diag.patch") diag = git(retroarch_root, "show", f"{RA_SOURCE}:pkg/ps5/chimera_ps5_diag.c") gfx = git(retroarch_root, "show", f"{RA_SOURCE}:gfx/drivers/sdl2_gfx.c") for token in ( "CHIMERA_PS5_FIRST_FRAME_INDEX 0u", "sceVideoOutSubmitFlip(", "submit_errno = submit_result != 0 ? errno : 0", "chimera_ps5_smoke_video_error(", ): if token not in patch: errors.append(f"artifact source token missing: {token}") if "stage == CHIMERA_PS5_DIAG_D12" not in diag: errors.append("D12 terminal source missing") if "chimera_ps5_early_diag_sdl_result(sdl_init_result)" not in gfx: errors.append("D04 caller source missing") if git(sdk_root, "rev-parse", "HEAD").strip() != SDK_COMMIT: errors.append("SDK commit mismatch") if git(sdl_root, "rev-parse", "HEAD").strip() != SDL_COMMIT: errors.append("SDL commit mismatch") if git(sdk_root, "status", "--porcelain"): errors.append("SDK tree is dirty") if git(sdl_root, "status", "--porcelain"): errors.append("SDL tree is dirty") stubs = (sdk_root / "sce_stubs/libSceVideoOut.c").read_text(encoding="utf-8") header = (sdl_root / "src/video/ps5/SDL_ps5video.h").read_text(encoding="utf-8") source = (sdl_root / "src/video/ps5/SDL_ps5video.c").read_text(encoding="utf-8") if "sceVideoOutSubmitFlip" not in stubs: errors.append("SDK submit export missing") if "int sceVideoOutSubmitFlip(int, int, uint32_t, int64_t);" not in header: errors.append("SDL submit prototype missing") if "static uint32_t frame_id = 0;" not in source or \ "sceVideoOutSubmitFlip(device_data->handle, idx, 1, frame_id)" not in source: errors.append("public SDL first-frame source mismatch") except (OSError, RuntimeError) as error: errors.append(str(error)) return errors def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--root", type=Path, required=True) parser.add_argument("--retroarch-root", type=Path, required=True) parser.add_argument("--sdk-root", type=Path, required=True) parser.add_argument("--sdl-root", type=Path, required=True) args = parser.parse_args() errors = validate_repository( args.root.resolve(), args.retroarch_root.resolve(), args.sdk_root.resolve(), args.sdl_root.resolve()) if errors: for error in errors: print(f"ERROR: {error}") return 1 print("Phase-1.0P offline VideoOut analysis validation passed") return 0 if __name__ == "__main__": raise SystemExit(main())