Skip to content

Commit a31a080

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 a31a080

2 files changed

Lines changed: 110 additions & 0 deletions

File tree

lib/cpu_policy.py

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