67 lines
3.2 KiB
Python
67 lines
3.2 KiB
Python
import json
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
|
|
scope = Path(sys.argv[1]).resolve()
|
|
disk = Path(sys.argv[2]).resolve()
|
|
with tempfile.TemporaryDirectory(prefix="scopeguest") as directory:
|
|
root = Path(directory)
|
|
transcript = root / "transcript"
|
|
command = [
|
|
"qemu-system-x86_64", "-machine", "pc,accel=tcg", "-cpu", "max",
|
|
"-m", "32M", "-display", "none", "-serial", "none", "-monitor", "none",
|
|
"-no-reboot", "-drive", f"format=raw,file={disk}",
|
|
"-debugcon", f"file:{transcript}", "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04",
|
|
]
|
|
try:
|
|
result = subprocess.run(command, capture_output=True, text=True, timeout=45)
|
|
except subprocess.TimeoutExpired:
|
|
print(transcript.read_text()[-2000:] if transcript.exists() else "no guest output", file=sys.stderr)
|
|
raise
|
|
assert result.returncode == 33, (result.returncode, result.stderr, transcript.read_text()[-2000:])
|
|
lines = transcript.read_text().splitlines()
|
|
assert lines[-1] == "done", "guest did not finish"
|
|
current = None
|
|
checks = 0
|
|
discrepancies = 0
|
|
for line in lines:
|
|
if line.startswith("begin "):
|
|
_, name, access, address, privilege = line.split()
|
|
current = []
|
|
elif line.startswith("context ") or line.startswith("page "):
|
|
assert current is not None
|
|
current.append(line)
|
|
elif line.startswith("observed "):
|
|
_, vector, error = line.split()
|
|
observed = int(vector), int(error)
|
|
elif line == "end":
|
|
assert current is not None
|
|
source = root / "source"
|
|
capture = root / "capture"
|
|
source.write_text("\n".join(current) + "\n")
|
|
subprocess.run([str(scope), "import", str(source), str(capture), "--source", f"qemu guest case {name}"],
|
|
check=True, capture_output=True, text=True)
|
|
result = subprocess.run([str(scope), "explain", str(capture), address, "--access",
|
|
["read", "write", "execute"][int(access)], "--privilege", privilege, "--json"],
|
|
capture_output=True, text=True, timeout=10)
|
|
assert result.returncode in (0, 1), (name, result.stderr)
|
|
prediction = json.loads(result.stdout)["result"]
|
|
if prediction["status"] == "mapped":
|
|
predicted = 0, 0
|
|
else:
|
|
predicted = prediction["fault"]["vector"], prediction["fault"]["error_code"]
|
|
if observed != predicted:
|
|
known = name in {"reserved", "nxe", "alignment", "pml"} and observed == (14, predicted[1] & ~1) and predicted == (14, 9)
|
|
assert known, (name, "observed", observed, "predicted", predicted, prediction)
|
|
discrepancies += 1
|
|
print(f"{name}: known tcg discrepancy, observed=0x{observed[1]:x} architectural=0x{predicted[1]:x}")
|
|
else:
|
|
print(f"{name}: vector={observed[0]} error=0x{observed[1]:x}")
|
|
checks += 1
|
|
current = None
|
|
assert checks == 31, checks
|
|
print(f"{checks - discrepancies} exact guest comparisons, {discrepancies} reserved-bit tcg discrepancies")
|