#!/usr/bin/env python3 # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2026 Open-IOV contributors # gpa.py - translate a guest virtual address to a guest physical address via # the QEMU monitor's HMP `gva2gpa`, reusing the QMP client in semfix_watch.py. # # Usage: sudo python3 tools/guest-diag/gpa.py [--offset] # guest VA, e.g. 0xffffc90000000000 or 0x7f... # --offset also print the /dev/shm/vgpu-guest-ram file offset # (gpa - 0x80000000) # # The monitor's `gva2gpa` translates with the selected CPU's CR3; while a CPU # runs user code under PTI the kernel half is hidden, so retry across CPUs # exactly like semfix_watch.Qmp.gva2gpa does. On failure the raw monitor text # is printed so the operator can see the reason (unmapped VA, no guest, ...). import argparse import os import re import sys HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) from semfix_watch import Qmp # noqa: E402 QMP_SOCK = os.environ.get("QMP_SOCK", "/tmp/qemu-qmp.sock") RAM_BASE = 0x80000000 def parse_va(text): text = text.strip() try: return int(text, 0) except ValueError: return int(text, 16) def translate(q, va): """Return (gpa, raw_hmp_text) or (None, last_raw_text).""" last = "" for cpu in q._cpu_order(): q._set_cpu(cpu) out = q.hmp("gva2gpa 0x%x" % va) last = out.strip() m = re.search(r"gpa:\s*0x([0-9a-fA-F]+)", out) if m: return int(m.group(1), 16), last return None, last def main(): ap = argparse.ArgumentParser(add_help=True) ap.add_argument("gva", help="guest virtual address (hex, 0x prefix optional)") ap.add_argument("--offset", action="store_true", help="also print gpa - 0x80000000 (guest RAM file offset)") ap.add_argument("--qmp", default=QMP_SOCK, help="QMP socket path") args = ap.parse_args() va = parse_va(args.gva) if not os.path.exists(args.qmp): print("gpa: QMP socket not found: %s" % args.qmp, file=sys.stderr) return 2 try: q = Qmp(args.qmp) except Exception as exc: # noqa: BLE001 - diagnostic surface print("gpa: cannot connect to %s: %s" % (args.qmp, exc), file=sys.stderr) return 2 gpa, text = translate(q, va) if gpa is None: print("gpa: gva2gpa 0x%x failed" % va, file=sys.stderr) if text: print(text, file=sys.stderr) return 1 print("GPA 0x%x" % gpa) if args.offset: print("OFFSET 0x%x" % (gpa - RAM_BASE)) return 0 if __name__ == "__main__": sys.exit(main())