1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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)
|