DF-5720 / drive_ddb.py
#!/usr/bin/env python3 """DF-5720 — scripted driver for the guest's socket-backed serial console. One persistent connection for the whole ddb session (QEMU applies backpressure to the guest UART once the chardev client buffer fills, so we drain continuously). Lines are sent in <=8-byte chunks with pacing: the guest's UART RX FIFO is 16 bytes and ddb polls cngetc without flow control. Sequence (see run.sh): enter ddb via sysctl over ssh, then at db> run the modifier test battery, then continue. """ import os, socket, sys, threading, time SOCK = "/tmp/opencode/df-5720-ser.sock" DRAIN = "/tmp/opencode/df-5720-drain.log" # socket-side copy of guest output def connect(): while True: try: s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.connect(SOCK) return s except (FileNotFoundError, ConnectionRefusedError) as e: print(f"connect failed: {e}; retrying...", flush=True) time.sleep(2) _drained = threading.Event() def drainer(s): with open(DRAIN, "ab", buffering=0) as d: _drained.set() while True: try: b = s.recv(65536) except OSError: return if not b: return d.write(b) def send_line(s, line): data = (line if line.endswith("\n") else line + "\n").encode() for i in range(0, len(data), 8): s.sendall(data[i:i+8]) time.sleep(0.15) # (command, seconds to wait for output after sending) SESSION = [ ("x/x db_active", 2), # control: letter-modifier hex dump ("x/4 db_active", 2), # numeric modifier on 'x' -> silently "x" ("examine/4 db_active", 4), # numeric modifier -> modif="examine" (!) ("print 0x41", 2), # control: default 'x' format ("print/4 0x41", 2), # numeric modifier -> modif="print" -> 'p' ("print/x 0x41", 2), # control: explicit letter modifier ("write/4 db_active 1", 3), # numeric modifier -> modif="write" -> error ("write/l db_active 1", 3), # control: letter modifier write works ("x/x db_active", 2), # post-write check (value unchanged: 1) ("c", 6), # continue out of ddb ] def main(): s = connect() t = threading.Thread(target=drainer, args=(s,), daemon=True) t.start(); _drained.wait() print("connected; drain ->", DRAIN, flush=True) time.sleep(2) # capture any console output already flowing for cmd, wait in SESSION: print(f">>> {cmd}", flush=True) send_line(s, cmd) time.sleep(wait) print("session script complete", flush=True) time.sleep(2) if __name__ == "__main__": main() |