#!/usr/bin/env python3 # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2026 Open-IOV contributors # tick_gen.py - keep the guest-RM timer tick page live in the host-side # /dev/shm/vgpu-guest-ram image. The guest's timer vmap page is read as a # u32 millisecond counter; writing a strictly increasing u32 there at 1 ms # (or --ms) keeps the guest RM's timer path moving. # # Usage: sudo python3 tools/guest-diag/tick_gen.py [--ms 1] [--file PATH] # guest physical address of the timer tick u32 (the guest's # timer vmap base + 0x6c004) # --ms N write interval in milliseconds (default 1) # --file P guest RAM file (default /dev/shm/vgpu-guest-ram) # # The file offset is gpa - 0x80000000. The mapping is created once; the loop # only writes the u32. SIGINT stops cleanly. import argparse import mmap import os import signal import struct import sys import time RAM_BASE = 0x80000000 DEFAULT_FILE = "/dev/shm/vgpu-guest-ram" stop = False def on_sigint(signum, frame): global stop stop = True def main(): ap = argparse.ArgumentParser(add_help=True) ap.add_argument("gpa", help="guest physical address (hex, 0x prefix optional)") ap.add_argument("--ms", type=float, default=1.0, help="write interval in milliseconds (default 1)") ap.add_argument("--file", default=DEFAULT_FILE, help="guest RAM file") args = ap.parse_args() try: gpa = int(args.gpa, 0) except ValueError: gpa = int(args.gpa, 16) if gpa < RAM_BASE: print("tick_gen: gpa 0x%x is below 0x%x" % (gpa, RAM_BASE), file=sys.stderr) return 2 off = gpa - RAM_BASE fd = os.open(args.file, os.O_RDWR) try: size = os.fstat(fd).st_size if off + 4 > size: print("tick_gen: offset 0x%x beyond %s (0x%x bytes)" % (off, args.file, size), file=sys.stderr) return 2 mm = mmap.mmap(fd, size, access=mmap.ACCESS_WRITE) try: signal.signal(signal.SIGINT, on_sigint) interval = max(0.0, args.ms / 1000.0) counter = time.monotonic_ns() // 1_000_000 iterations = 0 last_print = time.monotonic() print("tick_gen: file=%s gpa=0x%x off=0x%x interval=%.3fms" % (args.file, gpa, off, args.ms), flush=True) while not stop: now_ms = time.monotonic_ns() // 1_000_000 if now_ms <= counter: now_ms = counter + 1 counter = now_ms val = counter & 0xFFFFFFFF struct.pack_into("= 2.0: print("tick_gen: iterations=%d value=%d (0x%08x)" % (iterations, val, val), flush=True) last_print = t if interval: time.sleep(interval) print("tick_gen: stopped iterations=%d value=0x%08x" % (iterations, counter & 0xFFFFFFFF), flush=True) finally: mm.close() finally: os.close(fd) return 0 if __name__ == "__main__": try: sys.exit(main()) except KeyboardInterrupt: sys.exit(0)