diff options
Diffstat (limited to 'tools/guest-diag/tick_gen.py')
| -rwxr-xr-x | tools/guest-diag/tick_gen.py | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/tools/guest-diag/tick_gen.py b/tools/guest-diag/tick_gen.py new file mode 100755 index 0000000..d2c176b --- /dev/null +++ b/tools/guest-diag/tick_gen.py @@ -0,0 +1,97 @@ +#!/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 <gpa_hex> [--ms 1] [--file PATH] +# <gpa_hex> 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("<I", mm, off, val) + iterations += 1 + t = time.monotonic() + if t - last_print >= 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) |
