63 lines
3.7 KiB
Python
63 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Check real CPU registration, timer dispatch, missing-GIC refusal and 4K commpages."""
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--check-existing', action='store_true')
|
|
args = parser.parse_args()
|
|
start_symbol = '__ZN6ArmCpu5startEP9IOService'
|
|
cases = {
|
|
'start': ('darwinboot-pthread-armcpu-start', ['--break-symbol', start_symbol, '--finish-function']),
|
|
'timer': ('darwinboot-armcpu-timer', ['--break-symbol', '_rtclock_intr', '--finish-function']),
|
|
'missing_gic': ('darwinboot-armcpu-missing-gic', ['--missing-gic', '--break-symbol', start_symbol, '--finish-function']),
|
|
'commpage': ('darwinboot-armcpu-bsd-commpage', ['--break-symbol', '_bsd_init', '--check-commpage']),
|
|
}
|
|
reports = {}
|
|
for name, (directory, options) in cases.items():
|
|
output = ROOT / 'build' / directory
|
|
if not args.check_existing:
|
|
subprocess.run([sys.executable, ROOT / 'scripts/probe-darwinboot-xnu.py', '--armvirt', '--armcpu',
|
|
'--require-crypto14', '--output', output, *options], check=True)
|
|
reports[name] = json.loads((output / 'gdb-stop.json').read_text())
|
|
if len({report['kernel_sha256'] for report in reports.values()}) != 1:
|
|
raise SystemExit('CPU checks must use the same kernel')
|
|
for report in reports.values():
|
|
if (not report['native_4k_mmu_observed'] or not report['crypto14_initialization_observed']
|
|
or report.get('bounded_timeout_interrupted')):
|
|
raise SystemExit('Native 4K, real crypto and reached milestones are required')
|
|
pthread = report['pthread_globals']
|
|
if (pthread['_source_pthread_status'] != 0 or not pthread['_pthread_functions']
|
|
or not pthread['_pthread519_pthread_kern']):
|
|
raise SystemExit('Real pthread registration and callback exchange are required')
|
|
for name in ('start', 'timer', 'missing_gic'):
|
|
if reports[name]['pc'] != reports[name].get('function_return_target'):
|
|
raise SystemExit(f'{name} did not return to its actual caller')
|
|
start = reports['start']
|
|
if int(start['x0'], 16) != 1:
|
|
raise SystemExit('CPU start did not return true')
|
|
for name in ('start', 'timer'):
|
|
cpu = reports[name]['armcpu_globals']
|
|
if any(cpu[key] != 1 for key in ('_armcpu_registered', '_armcpu_gic_ready', '_gCPUsRunning')):
|
|
raise SystemExit(f'{name}: real CPU/GIC initialization incomplete')
|
|
if reports['timer']['armcpu_globals']['_armcpu_timer_interrupts'] < 1:
|
|
raise SystemExit('No real timer interrupt dispatched through rtclock_intr')
|
|
bad = reports['missing_gic']
|
|
if int(bad['x0'], 16) != 0 or any(bad['armcpu_globals'].values()):
|
|
raise SystemExit('Absent GIC must reject start without registering a running CPU')
|
|
if reports['commpage']['commpage_user64_shifts'] != {'_commpage_rw_addr': 12, '_commpage_kernel_ro_addr': 12}:
|
|
raise SystemExit('Both real user64 commpage aliases must advertise native 4K')
|
|
result = {'kernel_sha256': start['kernel_sha256'], 'cpu_start_returned_true': True,
|
|
'real_pthread_registration': True, 'rtclock_intr_returned': True,
|
|
'timer_interrupts': reports['timer']['armcpu_globals']['_armcpu_timer_interrupts'],
|
|
'missing_gic_refused': True, 'native_4k_observed': True,
|
|
'legacy_and_ro_commpage_user64_shifts': [12, 12],
|
|
'ipi_delivery_verified': False, 'hardware_boot_demonstrated': False,
|
|
'ipados_userspace_boot_demonstrated': False}
|
|
(ROOT / 'build/armcpu/test-report.json').write_text(json.dumps(result, indent=2) + '\n')
|
|
print('PASS: real CPU/pthread registration, timer dispatch/return, missing-GIC refusal, native 4K commpages')
|