Files
Acelogic 1510d8c6ce Snapintosh public release
An EL2 monitor/hypervisor and toolchain that boots macOS 27 on the ASUS A16
(Snapdragon X2 Elite), with an Adreno Metal plug-in and IOGPU kext, and
air2ir3, an AIR to Mesa ir3 shader compiler.

MIT, with GPL-2.0 components listed in LICENSE and THIRD_PARTY_NOTICES.md.
No Apple, Qualcomm, ASUS or Microsoft binaries are included.
2026-10-21 21:26:36 -04:00

72 lines
3.1 KiB
Python
Executable File

#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
"""Read the live GOP framebuffer through q1n1 and save a PNG; never draw."""
import argparse
from pathlib import Path
import sys
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / 'probes'))
from bootcontract import HostRuntime
from run import DEFAULT_DEVICE, connect, q1n1
def main():
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument('output', type=Path)
ap.add_argument('--device', default=DEFAULT_DEVICE)
ap.add_argument('--block-bytes', type=int, choices=(1024, 4096, 65536), default=65536,
help='maximum read payload; smaller blocks can help CDC links')
ap.add_argument('--reconnects', type=int, choices=range(4), default=0,
help='bounded total reconnects after failed read-only transfers')
args = ap.parse_args()
from PIL import Image
proxy = connect(args.device, tries=2)
try:
info = proxy.bootinfo()
fb = HostRuntime.from_q1n1(info, with_framebuffer=True).framebuffer
# Bound each USB transaction; one full-panel read can exceed the
# transport's request timeout before its checksum header arrives.
size = fb.stride * fb.height
block = args.block_bytes
identity_keys = ('image_base', 'stage_generation', 'fb_base', 'fb_width',
'fb_height', 'fb_stride', 'fb_format')
identity = tuple(info[key] for key in identity_keys)
remaining = args.reconnects
chunks = []
for offset in range(0, size, block):
count = min(block, size - offset)
while True:
try:
data = proxy.readmem(fb.base + offset, count)
if len(data) != count:
raise q1n1.ProxyError('short framebuffer read')
chunks.append(data)
break
except q1n1.ProxyError as error:
if not remaining:
raise
remaining -= 1
print(f'read at {offset:#x} failed ({error}); reconnecting, '
f'{remaining} retries remain', flush=True)
proxy.link.close()
proxy = None
proxy = connect(args.device, tries=2)
retry_info = proxy.bootinfo()
if tuple(retry_info[key] for key in identity_keys) != identity:
raise RuntimeError('target or framebuffer changed during capture')
pixels = b''.join(chunks)
image = Image.frombytes('RGB', (fb.width, fb.height), pixels, 'raw',
'BGRX' if info['fb_format'] == 1 else 'RGBX', fb.stride)
args.output.parent.mkdir(parents=True, exist_ok=True)
image.save(args.output)
print(f'{args.output.resolve()}: {fb.width}x{fb.height}, '
f'{len(pixels)} framebuffer bytes read; no pixels written')
finally:
if proxy is not None:
proxy.link.close()
if __name__ == '__main__':
main()