~james/scope

scope/guest/verify.py -rw-r--r-- 3.2 KiB
6e057d53 — james chore(import): snapshot james.dad.town/scope at 034e6b67d44c 16 hours ago
                                                                                
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
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")