Skip to content

Commit 831e049

Browse files
committed
Add cpu policy helper and test
Add a CPU policy tooling to be able to check for certain CPU feature. The test_cpu_policy also logs hosts CPU policies for debugging purposes. Signed-off-by: Teddy Astie <teddy.astie@vates.tech>
1 parent b2b7612 commit 831e049

2 files changed

Lines changed: 108 additions & 0 deletions

File tree

lib/cpu_policy.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
from dataclasses import dataclass
2+
3+
from lib.host import Host
4+
5+
NO_SUBLEAF = 0xffffffff
6+
7+
@dataclass(frozen=True)
8+
class CpuidRegisters:
9+
eax: int
10+
ebx: int
11+
ecx: int
12+
edx: int
13+
14+
class CpuPolicy:
15+
"""A specific CPU policy (PV, HVM, ...)"""
16+
# (leaf, subleaf) -> (eax, ebx, ecx, edx)
17+
cpuid: dict[tuple[int, int], CpuidRegisters]
18+
# msr -> val
19+
msr: dict[int, int]
20+
21+
def __init__(self, cpuid: dict[tuple[int, int], CpuidRegisters], msr: dict[int, int]):
22+
self.cpuid = cpuid
23+
self.msr = msr
24+
25+
class HostCpuPolicy:
26+
"""All CPU policies of a host (CPUID, specific MSRs)"""
27+
policies: dict[str, CpuPolicy] = {}
28+
29+
def __init__(self, host: Host):
30+
text = host.ssh("xen-cpuid --policy")
31+
current_policy: CpuPolicy | None = None
32+
mode: Literal["cpuid", "msr"] | None = None
33+
34+
lines = text.splitlines()
35+
36+
for line in lines:
37+
line = line.rstrip()
38+
39+
# ---- Policy header ----
40+
if "policy:" in line:
41+
# Example: "Raw policy: 32 leaves, 2 MSRs"
42+
name, rest = line.split("policy:", 1)
43+
name = name.strip()
44+
45+
self.policies[name] = CpuPolicy(dict(), dict())
46+
current_policy = self.policies[name]
47+
mode = None
48+
continue
49+
50+
if current_policy is None:
51+
continue
52+
53+
# ---- Section switches ----
54+
if line.strip() == "CPUID:":
55+
mode = "cpuid"
56+
continue
57+
58+
if line.strip() == "MSRs:":
59+
mode = "msr"
60+
continue
61+
62+
# Skip table headers
63+
if "leaf" in line or "index" in line or not line.strip():
64+
continue
65+
66+
# ---- CPUID parsing ----
67+
if mode == "cpuid":
68+
# Example:
69+
# 00000004:00000003 -> 1c03c163:02c0003f:00001fff:00000006
70+
left, right = line.split("->")
71+
leaf_hex, subleaf_hex = left.strip().split(":")
72+
eax, ebx, ecx, edx = right.strip().split(":")
73+
74+
key = (int(leaf_hex, 16), int(subleaf_hex, 16))
75+
current_policy.cpuid[key] = CpuidRegisters(
76+
int(eax, 16),
77+
int(ebx, 16),
78+
int(ecx, 16),
79+
int(edx, 16)
80+
)
81+
continue
82+
83+
# ---- MSR parsing ----
84+
if mode == "msr":
85+
# Example:
86+
# 0000010a -> 400000000c000000
87+
idx_hex, val_hex = line.split("->")
88+
idx = int(idx_hex.strip(), 16)
89+
val = int(val_hex.strip(), 16)
90+
current_policy.msr[idx] = val
91+
continue

tests/xen/test_cpu_policy.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import pytest
2+
import logging
3+
4+
from lib.host import Host
5+
from lib.cpu_policy import NO_SUBLEAF, HostCpuPolicy
6+
7+
class TestCpuPolicy:
8+
def test_cpu_policy(self, host: Host) -> None:
9+
cpu_policy = HostCpuPolicy(host)
10+
11+
for name, policy in cpu_policy.policies.items():
12+
for ((leaf, subleaf), regs) in policy.cpuid.items():
13+
eax, ebx, ecx, edx = regs.eax, regs.ebx, regs.ecx, regs.edx
14+
logging.info(f"CPUID[{name}]: {leaf:08x}:{subleaf:08x} -> {eax:08x}:{ebx:08x}:{ecx:08x}:{edx:08x}")
15+
16+
for (msr, val) in policy.msr.items():
17+
logging.info(f"MSR [{name}]: {msr:08x} -> {val:016x}")

0 commit comments

Comments
 (0)