Files
UniXNU/scripts/check-system-framebuffer-client.py
T
rpone 02f8cdd377 fix(scripts): use mach.development.bcm2837.unstripped (no .noctf suffix)
The kernel object produced by the ipados27 build tree is
mach.development.bcm2837.unstripped; the .noctf variant is not emitted.
Fix the reference in build-sheng-source-drivers.py and all other scripts
that contained the wrong suffix.

Also update submodule pointers for Adreno740 (preemption A1) and
AdrenoMetal (priority forwarding + pass reporter), and regenerate
turnip-gmem-mutable-swap.patch to include the NativeAdrenoAbiPriority
method-17 call in tu_knl_darwin.cc.
2026-11-01 00:20:19 +08:00

152 lines
12 KiB
Python

#!/usr/bin/env python3
"""Verify real GOP display discovery and the unchanged original client factory."""
import argparse
import hashlib
import mmap
import struct
import json
from pathlib import Path
import subprocess
import sys
ROOT=Path(__file__).resolve().parents[1];OUT=ROOT/'build/native-legacy-display'
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--profile',choices=['pitch','info','scale'],default='scale');args=parser.parse_args()
sys.path.insert(0,str(ROOT/'kits/XnuPrelink'))
from native_prelink import macho,backed_offset,unserialize_xml
def require(v,reason):
if not v:raise SystemExit(reason)
def sha(p):return hashlib.sha256(Path(p).read_bytes()).hexdigest()
build=json.loads((OUT/'build-report.json').read_text())
for name,digest in build['source_sha256'].items():require(sha(ROOT/name)==digest,'Exact current compiled source '+name)
revision=subprocess.check_output(['git','-C',ROOT/'drivers/GopFramebuffer','rev-parse','HEAD'],text=True).strip()
for name in ['NativeLegacyDisplay.cpp','NativeGOPPresenter.cpp','NativeGOPWire.h']:
data=subprocess.check_output(['git','-C',ROOT/'drivers/GopFramebuffer','show',revision+':'+name])
require(hashlib.sha256(data).hexdigest()==sha(ROOT/'drivers/GopFramebuffer'/name),'Exact committed driver source')
require(sha(OUT/'libNativeLegacyDisplay.late.a')==build['archive_sha256'] and
sha(OUT/'OriginalLegacyFramebufferABI.h')==build['generated_abi_sha256'],'Exact late archive and generated ABI')
require(build['virtual_entries']==358 and build['owned_entries']==[0,1,7,14,84,87,141,184,188,211,213,245,246,288,306,307,337,338,345],
'Exact qualified own overrides and remaining guarded leaves')
audit=json.loads((ROOT/'build/display-analysis/mobile-graphics-legacy-abi.json').read_text())
require(int(build['bindings']['__ZTV25IOMobileFramebufferLegacy'],16)==int(audit['legacy_vptr'],16)-16,
'Apple-kext qualified super calls bind the real original vtable header')
candidate=ROOT/('build/xnu-prelink/native-owner-legacy-display-'+args.profile+'-candidate.macho');plan=json.loads(candidate.with_suffix('.json').read_text())
raw=candidate.read_bytes();image=macho(raw)
require(sha(candidate)==plan['candidate_sha256'] and sha(ROOT/'build/xnu/ios-obj/DEVELOPMENT_ARM64_BCM2837/mach.development.bcm2837.unstripped')==plan['native_sha256'],
'Exact current source kernel and original-family candidate')
for module,digest in plan['original_executable_sha256'].items():
extent=next(e for e in plan['extents'] if e['module']==module and e['segment']=='__TEXT_EXEC')
at=backed_offset(image['segments'],extent['va'],extent['size'],len(raw))
require(hashlib.sha256(raw[at:at+extent['size']]).hexdigest()==digest,'Whole original executable unchanged: '+module)
# Qualify the actual uint32 output / IOReturn slot-188 contract and its
# missing-property result, rather than giving an opaque method a guessed ABI.
for address,size,digest in [
(0xfffffff005798184,312,'6c161fa48818f254191d915cc99318e085a1555d7ddbd003b86ccd75034ba363'),
(0xfffffff005778168,56,'b925b4b5bb870637ace1f46bc14010bf5d473edbeb6eaecd9d898fedbc8ee465'),
(0xfffffff00577a554,88,'6d8ebdd3591475c418f3632d112cad88a12cc4a53fa6733190531aa0e56d46c8')]:
at=backed_offset(image['segments'],address,size,len(raw))
require(hashlib.sha256(raw[at:at+size]).hexdigest()==digest,'Exact original dot-pitch implementation/callers')
pitch_path=ROOT/('build/darwinboot-owner-hvf-legacy-display-start-'+args.profile+'-dotpitch/gdb-stop.json')
pitch=json.loads(pitch_path.read_text())
require(pitch['kernel_sha256']==plan['candidate_sha256'] and not pitch['bounded_timeout_interrupted'] and
int(pitch['pc'],16)==0xfffffff005778194 and int(pitch['x0'],16)==0xe00002f0,
'Actual original caller receives genuine missing dot-pitch error')
frame=pitch['frame_chain'][1];data=bytes.fromhex(frame['local_bytes'])
offset=int(pitch['frame_chain'][0]['fp'],16)+16-int(frame['local_address'],16)
require(data[offset:offset+4]==bytes(4),'Actual uint32 output is initialized to zero on missing metadata')
require(pitch['native_legacy_display']['client_calls']>=2 and not pitch['native_legacy_display']['guard_slot'],
'Real system clients reach the query without a backend guard')
frame_info_observation=None
if args.profile in ('info','scale'):
require(build['guarded_backend_leaves']==[211,245,246,306,307,337,338,345],
'Eight unclassified leaves remain guarded; optional frame tracing is explicitly unsupported')
for address,size,digest in [
(0xfffffff005778688,152,'1dda59338b977c6c2ea0556d397e5ae26ace30d6c8c86fdceea18036ffb5b4c7'),
(0xfffffff00577a718,88,'9ffaf7219e50c6134bdc72fe51eff26de43ed27de4fe37fd0a05fff68771dc84'),
(0xfffffff005f35bb0,24,'75e1cd2260e703a28d2a5e7cf2ed1bbe0b1d4b49359860c12d55634dc85a805b')]:
at=backed_offset(image['segments'],address,size,len(raw))
require(hashlib.sha256(raw[at:at+size]).hexdigest()==digest,'Exact original frame-info ABI and selector 74 dispatch')
# Original userspace enumerates fixed 64-byte key names with strndup;
# failure / zero count follows its real optional-capability path.
cache=ROOT/'.cache/firmware/target-volumes/systemos-mount/System/Library/Caches/com.apple.dyld'
wanted={0x1b63d53f8:(432,'6a6d646a0ffbbd9ff528d7ec73b7b451af4f33c765ceb02c06c6bffdad77d881'),
0x1b63d65b0:(172,'e506fe8d7ec92144364bcdbcc5bf519569da6f30b319e24ccfc9a44803a228af')}
found=set()
for path in sorted(cache.glob('dyld_shared_cache_arm64*')):
if path.suffix=='.symbols':continue
with path.open('rb') as stream,mmap.mmap(stream.fileno(),0,access=mmap.ACCESS_READ) as mapping:
require(mapping[:16].rstrip(bytes(1))==b'dyld_v1 arm64','Original ARM64 shared-cache format')
table,count=struct.unpack_from('<2I',mapping,16)
for index in range(count):
va,size,offset,_,protection=struct.unpack_from('<3Q2I',mapping,table+index*32)
for address,(length,digest) in wanted.items():
if va<=address and address+length<=va+size:
at=offset+address-va
require(protection&4 and hashlib.sha256(mapping[at:at+length]).hexdigest()==digest,
'Exact unchanged original userspace trace-key enumeration')
found.add(address)
require(found==set(wanted),'Both original userspace frame-info functions are present')
path=ROOT/('build/darwinboot-owner-hvf-legacy-display-start-'+args.profile+'-frameinfo-return/gdb-stop.json')
d=json.loads(path.read_text())
require(d['kernel_sha256']==plan['candidate_sha256'] and not d['bounded_timeout_interrupted'] and
int(d['pc'],16)==0xfffffff0057786e8 and int(d['x0'],16)==0xe00002c7,
'Original frame-info caller receives genuine unsupported status')
data=bytes.fromhex(d['stack_bytes']);require(data[16:24]==bytes(8),'Both actual frame-info scalar outputs are zero')
require(bytes.fromhex(d['frame_indirect_memory']['0:-20:40']['bytes'])==bytes(64),'Actual fixed-size trace-key output is cleared')
require(not d['native_legacy_display']['guard_slot'] and d['native_legacy_display']['client_calls']>=2,
'Optional frame-info query returns after actual system-client creation')
frame_info_observation=dict(checkpoint_sha256=sha(path),status=0xe00002c7,trace_keys_supported=False)
observations={}
for case,directory in [('client','start-'+args.profile+'-client'),('init','init-'+args.profile+'-control'),('default-off','default-off-'+args.profile+'-control')]:
path=ROOT/('build/darwinboot-owner-hvf-legacy-display-'+directory+'/gdb-stop.json');d=json.loads(path.read_text())
require(d['kernel_sha256']==plan['candidate_sha256'] and not d['bounded_timeout_interrupted'] and d['qemu_accelerator']=='hvf','Actual bounded same-candidate observation '+case)
regs={k:int(v,16) for k,v in d['system_registers'].items()}
require(d['native_4k_mmu_observed'] and (regs['TCR_EL1']>>14)&3==0 and (regs['TCR_EL1']>>30)&3==2 and
regs['SCTLR_EL1']&1 and regs['TTBR0_EL1'] and regs['TTBR1_EL1'],'Actual native 4K CPU translation')
state=d['native_legacy_display'];require(not state['guard_slot'],'No unclassified backend called before this boundary')
if args.profile=='scale':
require(state['unit_scale_calls']==(1 if case=='client' else 0) and
state['chosen_scale']==(1 if case=='client' else 0) and not state['chosen_rotation'] and
(state['unit_scale_status']==0 if case=='client' else state['unit_scale_status']!=0),
'Actual unit-scale metadata is published only after the explicit mapped GOP system experiment')
if case=='init':
require(state['stage']==8 and state['released']==1 and state['init_result']==state['parent_match']==1 and
state['object_size']==6320 and not state['registration_calls'] and not state['dt_attached'] and not state['client_calls'],
'Real initialized derived object releases through original free without publication')
elif case=='client':
require(d['stop_symbol']=='_native_legacy_client_checkpoint' and state['stage']==10 and
state['init_result']==state['start_result']==state['probe_match']==state['parent_match']==1 and
state['object_size']==6320 and state['registration_calls']==state['dt_attached']==1 and not state['released'],
'Original start completes and actual GOP subsystem is registered/attached')
video=d['actual_boot_video']['pe_video']
require(state['gop_map'] and state['gop_virtual'] and state['gop_physical']==video['v_baseAddr']==state['video_base'] and
state['gop_bytes']==state['video_bytes']==video['v_rowBytes']*video['v_height']==1920000 and
state['gop_width']==video['v_width']==800 and state['gop_height']==video['v_height']==600,
'Actual retained GOP mapping and original mode')
props=unserialize_xml(d['native_legacy_properties'].encode())
require(props['IOMFB Debug Info']['Global']['width']==800 and props['IOMFB Debug Info']['Global']['height']==600 and
'NativeGOPFramebufferMapping' not in props,'Real original property serialization with no unencodable mapping object')
require(state['client_calls']==1 and state['client_type']==state['client_status']==0 and state['client_pid']>1 and
state['client_task'] and state['client_object'] and d['native_legacy_client_class']=='IOMobileFramebufferUserClient',
'Original system process creates the genuine original userclient through original newUserClient')
else:
require(d['original_launchd_main_executed'] and d['followed_main_entry']['first_instruction_completed'] and
state['stage']==0 and state['meta_status']==-1 and not state['registration_calls'] and not state['dt_attached'] and not state['client_calls'],
'Default-off original launchd without display experiment')
if case!='init':
require(d['original_amfi_table_owner_observed'] and d['owner_root_binding_audit']['boot_root_accepted'] and
d['owner_root_binding_audit']['_native_owner_root_apple_autherr']==80 and
d['actual_signature_failures']==dict(_cs_validate_page_bad_hash=0,_cs_validate_page_no_hash=0),
'Original AMFI/Apple rejection and independent owner-root acceptance')
shifts=d['commpage_user64_shifts'] if case=='client' else d['direct_user_commpage_shifts']
require(set(shifts.values())=={12},'Actual native user page ABI')
observations[case]=dict(checkpoint_sha256=sha(path),state=state,actual_tcr=regs['TCR_EL1'])
(OUT/'system-client-checkpoint.json').write_text(json.dumps(dict(candidate_sha256=plan['candidate_sha256'],native_sha256=plan['native_sha256'],
driver_revision=revision,observations=observations,frame_info=frame_info_observation,dot_pitch_checkpoint_sha256=sha(pitch_path),missing_dot_pitch_return=0xe00002f0,actual_original_start=True,actual_system_client_created=True,
original_instructions_modified=False,swap_contract_complete=False,system_metal_device=False,original_desktop=False,
adreno_execution=False,hardware_tested=False),indent=2)+'\n')
print('PASS: real GOP subsystem discovery, original framebuffer start and original system-process userclient creation')
print('Init/release and default-off controls, original AMFI/root/native 4K pass; actual swaps, system Metal and desktop remain open')