97 lines
4.4 KiB
Python
97 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
||
"""Vérifie le reset des périphériques d’affichage de la machine, CPU arrêté et NAND en lecture seule."""
|
||
import argparse
|
||
import json
|
||
from pathlib import Path
|
||
import socket
|
||
import subprocess
|
||
import tempfile
|
||
import time
|
||
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(description=__doc__)
|
||
root = Path(__file__).resolve().parents[3]
|
||
parser.add_argument('--qemu', type=Path, default=root / 'build/qemu-system-arm')
|
||
parser.add_argument('--firmware', type=Path, required=True, help='dossier avec bootrom, nor.bin et nand/')
|
||
args = parser.parse_args()
|
||
fw = args.firmware.resolve()
|
||
with tempfile.TemporaryDirectory(prefix='ipod-reset-') as directory:
|
||
directory = Path(directory)
|
||
paths = [directory / name for name in ['qmp.sock', 'qtest.sock']]
|
||
with (directory / 'qemu.log').open('w+') as log:
|
||
proc = subprocess.Popen([
|
||
str(args.qemu.resolve()), '-M',
|
||
f'iPod-Touch,bootrom={fw}/bootrom,nor={fw}/nor.bin,nand={fw}/nand,legacy-nor=off,nand-write=off',
|
||
'-cpu', 'max', '-m', '2G', '-S', '-display', 'none', '-serial', 'null', '-serial', 'null',
|
||
'-qmp', f'unix:{paths[0]},server=on,wait=off',
|
||
'-qtest', f'unix:{paths[1]},server=on,wait=off'], stdout=log, stderr=log)
|
||
connections = []
|
||
try:
|
||
deadline = time.monotonic() + 10
|
||
while not all(p.exists() for p in paths):
|
||
if proc.poll() is not None or time.monotonic() >= deadline:
|
||
log.seek(0)
|
||
raise RuntimeError(log.read())
|
||
time.sleep(.02)
|
||
for path in paths:
|
||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||
sock.settimeout(5)
|
||
sock.connect(str(path))
|
||
connections.append((sock, sock.makefile('rwb')))
|
||
qmp, test = [f for _, f in connections]
|
||
json.loads(qmp.readline())
|
||
|
||
def command(name):
|
||
qmp.write((json.dumps({'execute': name}) + '\n').encode())
|
||
qmp.flush()
|
||
while True:
|
||
r = json.loads(qmp.readline())
|
||
if 'error' in r:
|
||
raise RuntimeError(r['error'])
|
||
if 'return' in r:
|
||
return r['return']
|
||
|
||
def access(line):
|
||
test.write((line + '\n').encode())
|
||
test.flush()
|
||
r = test.readline().decode().strip()
|
||
assert r.startswith('OK'), r
|
||
return int(r.split()[1], 0) if len(r.split()) > 1 else None
|
||
|
||
command('qmp_capabilities')
|
||
# Registres d'affichage de la machine réelle : le reset doit atteindre MIPI.
|
||
regs = [0x38900004, 0x38900008, 0x38900024, 0x3d800008,
|
||
0x39100004, 0x39200004, 0x39300000, 0x39300284]
|
||
rom = access('readl 0')
|
||
for cycle in range(3):
|
||
for address in regs:
|
||
access(f'writel {address:#x} 0x1234')
|
||
assert access(f'readl {address:#x}') == 0x1234, hex(address)
|
||
# Abandonner une lecture au milieu de la réponse d'identification de la dalle.
|
||
assert access('readl 0x3d80003c') == 0x31a
|
||
command('system_reset')
|
||
for address in regs:
|
||
assert access(f'readl {address:#x}') == 0, hex(address)
|
||
assert access('readl 0x3d80003c') == 0x31a
|
||
assert access('readl 0x3d80003c') == 0x00a1d13c
|
||
assert access('readl 0') == rom
|
||
print('Trois resets d’affichage : registres effacés, réponse MIPI réinitialisée, BootROM conservée.')
|
||
command('quit')
|
||
proc.wait(timeout=5)
|
||
finally:
|
||
for sock, stream in connections:
|
||
stream.close()
|
||
sock.close()
|
||
if proc.poll() is None:
|
||
proc.terminate()
|
||
try:
|
||
proc.wait(timeout=5)
|
||
except subprocess.TimeoutExpired:
|
||
proc.kill()
|
||
proc.wait()
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|