xref: /openbmc/smbios-mdr/tools/sst-compare-redfish-os.py (revision defbc2acdbdf7827706ddd7fee53d193b3d849b3)
194c94bfbSJonathan Doman#!/usr/bin/env python3
294c94bfbSJonathan Doman
394c94bfbSJonathan Doman# This tool runs on the host CPU and gathers all SST related configuration from
494c94bfbSJonathan Doman# the BMC (Redfish) and from the linux driver, and compares them to catch any
594c94bfbSJonathan Doman# errors or disagreement. Only required arguments are the details to start a
694c94bfbSJonathan Doman# Redfish session.
794c94bfbSJonathan Doman#
894c94bfbSJonathan Doman# This was tested running on a live Arch Linux ISO environment. Any Linux
994c94bfbSJonathan Doman# installation should work, but best to get the latest tools and kernel driver.
1094c94bfbSJonathan Doman#
1194c94bfbSJonathan Doman# Required dependencies:
1294c94bfbSJonathan Doman# * DMTF's redfish python library. This is available in pip.
1394c94bfbSJonathan Doman# * intel-speed-select tool from the kernel source tree
1494c94bfbSJonathan Doman#   (tools/power/x86/intel-speed-select), and available in the PATH.
1594c94bfbSJonathan Doman
1694c94bfbSJonathan Domanimport argparse
1794c94bfbSJonathan Domanimport json
1894c94bfbSJonathan Domanimport re
1994c94bfbSJonathan Domanimport subprocess
2094c94bfbSJonathan Domanimport sys
2194c94bfbSJonathan Doman
22f5a2d2adSPatrick Williamsimport redfish
23f5a2d2adSPatrick Williams
2494c94bfbSJonathan Domanlinux_cpu_map = dict()
2594c94bfbSJonathan Domansuccess = True
2694c94bfbSJonathan Doman
27f5a2d2adSPatrick Williams
28*defbc2acSJonathan Domandef filter_powerdomains(sst_data):
29*defbc2acSJonathan Doman    # For TPMI-based CPUs, we only care about powerdomain-0
30*defbc2acSJonathan Doman    cpus = list(sst_data.keys())
31*defbc2acSJonathan Doman    for proc in cpus:
32*defbc2acSJonathan Doman        match = re.search("powerdomain-(\\d+)", proc)
33*defbc2acSJonathan Doman        if not match or match.group(1) == "0":
34*defbc2acSJonathan Doman            continue
35*defbc2acSJonathan Doman        del sst_data[proc]
36*defbc2acSJonathan Doman
37*defbc2acSJonathan Doman
3894c94bfbSJonathan Domandef get_linux_output():
39f5a2d2adSPatrick Williams    cmd = [
40f5a2d2adSPatrick Williams        "/usr/bin/env",
41f5a2d2adSPatrick Williams        "intel-speed-select",
42f5a2d2adSPatrick Williams        "--debug",
43f5a2d2adSPatrick Williams        "--format=json",
44f5a2d2adSPatrick Williams        "perf-profile",
45f5a2d2adSPatrick Williams        "info",
46f5a2d2adSPatrick Williams    ]
4794c94bfbSJonathan Doman    process = subprocess.run(cmd, capture_output=True, text=True)
4894c94bfbSJonathan Doman    process.check_returncode()
4994c94bfbSJonathan Doman    result = json.loads(process.stderr)
50*defbc2acSJonathan Doman    filter_powerdomains(result)
5194c94bfbSJonathan Doman
5294c94bfbSJonathan Doman    global linux_cpu_map
5394c94bfbSJonathan Doman    linux_cpu_map = dict()
54f5a2d2adSPatrick Williams    for line in process.stdout.split("\n"):
55f5a2d2adSPatrick Williams        match = re.search("logical_cpu:(\\d+).*punit_core:(\\d+)", line)
5694c94bfbSJonathan Doman        if not match:
5794c94bfbSJonathan Doman            continue
5894c94bfbSJonathan Doman        logical_thread = int(match.group(1))
5994c94bfbSJonathan Doman        physical_core = int(match.group(2))
6094c94bfbSJonathan Doman        linux_cpu_map[logical_thread] = physical_core
6194c94bfbSJonathan Doman
62f5a2d2adSPatrick Williams    cmd = [
63f5a2d2adSPatrick Williams        "/usr/bin/env",
64f5a2d2adSPatrick Williams        "intel-speed-select",
65f5a2d2adSPatrick Williams        "--format=json",
66f5a2d2adSPatrick Williams        "perf-profile",
67f5a2d2adSPatrick Williams        "get-config-current-level",
68f5a2d2adSPatrick Williams    ]
6994c94bfbSJonathan Doman    process = subprocess.run(cmd, capture_output=True, text=True)
7094c94bfbSJonathan Doman    current_level = json.loads(process.stderr)
71*defbc2acSJonathan Doman    filter_powerdomains(current_level)
7294c94bfbSJonathan Doman
7394c94bfbSJonathan Doman    for proc, data in current_level.items():
7494c94bfbSJonathan Doman        result[proc].update(data)
7594c94bfbSJonathan Doman
7694c94bfbSJonathan Doman    return result
7794c94bfbSJonathan Doman
7894c94bfbSJonathan Doman
7994c94bfbSJonathan Domandef compare(redfish_val, linux_val, description):
8094c94bfbSJonathan Doman    err = ""
81a30229e1SJonathan Doman    if None in (redfish_val, linux_val):
82a30229e1SJonathan Doman        err = "MISSING VALUE"
83a30229e1SJonathan Doman    elif redfish_val != linux_val:
8494c94bfbSJonathan Doman        err = "!! MISMATCH !!"
8594c94bfbSJonathan Doman        global success
8694c94bfbSJonathan Doman        success = False
8794c94bfbSJonathan Doman    print(f"{description}: {err}")
8894c94bfbSJonathan Doman    print(f"  Redfish: {redfish_val}")
8994c94bfbSJonathan Doman    print(f"  Linux: {linux_val}")
9094c94bfbSJonathan Doman
9194c94bfbSJonathan Doman
9294c94bfbSJonathan Domandef get_linux_package(linux_data, redfish_id):
93f5a2d2adSPatrick Williams    match = re.match("cpu(\\d+)", redfish_id)
9494c94bfbSJonathan Doman    if not match:
9594c94bfbSJonathan Doman        raise RuntimeError(f"Redfish CPU name is unexpected: {redfish_id}")
9694c94bfbSJonathan Doman    num = match.group(1)
9794c94bfbSJonathan Doman    matching_keys = []
9894c94bfbSJonathan Doman    for key in linux_data.keys():
9994c94bfbSJonathan Doman        if re.match(f"^package-{num}:.*", key):
10094c94bfbSJonathan Doman            matching_keys.append(key)
10194c94bfbSJonathan Doman    if len(matching_keys) != 1:
102f5a2d2adSPatrick Williams        raise RuntimeError(
103f5a2d2adSPatrick Williams            f"Unexpected number of matching linux objects for {redfish_id}"
104f5a2d2adSPatrick Williams        )
10594c94bfbSJonathan Doman    return linux_data[matching_keys[0]]
10694c94bfbSJonathan Doman
10794c94bfbSJonathan Doman
10894c94bfbSJonathan Domandef compare_config(redfish_config, linux_config):
10994c94bfbSJonathan Doman    print(f"--Checking {redfish_config['Id']}--")
110f5a2d2adSPatrick Williams    compare(
111f5a2d2adSPatrick Williams        redfish_config["BaseSpeedMHz"],
112f5a2d2adSPatrick Williams        int(linux_config["base-frequency(MHz)"]),
113f5a2d2adSPatrick Williams        "Base Speed",
114f5a2d2adSPatrick Williams    )
11594c94bfbSJonathan Doman
11694c94bfbSJonathan Doman    actual_hp_p1 = actual_lp_p1 = 0
11794c94bfbSJonathan Doman    actual_hp_cores = set()
11894c94bfbSJonathan Doman    for bf in redfish_config["BaseSpeedPrioritySettings"]:
11994c94bfbSJonathan Doman        if not actual_hp_p1 or bf["BaseSpeedMHz"] > actual_hp_p1:
12094c94bfbSJonathan Doman            actual_hp_p1 = bf["BaseSpeedMHz"]
12194c94bfbSJonathan Doman            actual_hp_cores = set(bf["CoreIDs"])
12294c94bfbSJonathan Doman        if not actual_lp_p1 or bf["BaseSpeedMHz"] < actual_lp_p1:
12394c94bfbSJonathan Doman            actual_lp_p1 = bf["BaseSpeedMHz"]
12494c94bfbSJonathan Doman
12594c94bfbSJonathan Doman    exp_hp_p1 = exp_lp_p1 = 0
12694c94bfbSJonathan Doman    exp_hp_cores = set()
12794c94bfbSJonathan Doman    if "speed-select-base-freq-properties" in linux_config:
12894c94bfbSJonathan Doman        exp_bf_props = linux_config["speed-select-base-freq-properties"]
12994c94bfbSJonathan Doman        exp_hp_p1 = int(exp_bf_props["high-priority-base-frequency(MHz)"])
130f5a2d2adSPatrick Williams        exp_hp_cores = set(
131f5a2d2adSPatrick Williams            map(
132f5a2d2adSPatrick Williams                lambda x: linux_cpu_map[x],
133f5a2d2adSPatrick Williams                map(int, exp_bf_props["high-priority-cpu-list"].split(",")),
134f5a2d2adSPatrick Williams            )
135f5a2d2adSPatrick Williams        )
13694c94bfbSJonathan Doman        exp_lp_p1 = int(exp_bf_props["low-priority-base-frequency(MHz)"])
13794c94bfbSJonathan Doman
13894c94bfbSJonathan Doman    compare(actual_hp_p1, exp_hp_p1, "SST-BF High Priority P1 Freq")
13994c94bfbSJonathan Doman    compare(actual_hp_cores, exp_hp_cores, "SST-BF High Priority Core List")
14094c94bfbSJonathan Doman    compare(actual_lp_p1, exp_lp_p1, "SST-BF Low Priority P1 Freq")
14194c94bfbSJonathan Doman
142f5a2d2adSPatrick Williams    compare(
143f5a2d2adSPatrick Williams        redfish_config["MaxJunctionTemperatureCelsius"],
14494c94bfbSJonathan Doman        int(linux_config["tjunction-max(C)"]),
145f5a2d2adSPatrick Williams        "Junction Temperature",
146f5a2d2adSPatrick Williams    )
147a30229e1SJonathan Doman    # There is no equivalent value in linux for the per-level P0_1 freq.
148f5a2d2adSPatrick Williams    compare(redfish_config["MaxSpeedMHz"], None, "SSE Max Turbo Speed")
149f5a2d2adSPatrick Williams    compare(
150f5a2d2adSPatrick Williams        redfish_config["TDPWatts"],
15194c94bfbSJonathan Doman        int(linux_config["thermal-design-power(W)"]),
152f5a2d2adSPatrick Williams        "TDP",
153f5a2d2adSPatrick Williams    )
154f5a2d2adSPatrick Williams    compare(
155f5a2d2adSPatrick Williams        redfish_config["TotalAvailableCoreCount"],
15694c94bfbSJonathan Doman        int(linux_config["enable-cpu-count"]) // 2,
157f5a2d2adSPatrick Williams        "Enabled Core Count",
158f5a2d2adSPatrick Williams    )
15994c94bfbSJonathan Doman
160f5a2d2adSPatrick Williams    actual_turbo = [
161f5a2d2adSPatrick Williams        (x["ActiveCoreCount"], x["MaxSpeedMHz"])
162f5a2d2adSPatrick Williams        for x in redfish_config["TurboProfile"]
163f5a2d2adSPatrick Williams    ]
164*defbc2acSJonathan Doman    linux_turbo = (
165*defbc2acSJonathan Doman        linux_config.get("turbo-ratio-limits-sse")
166*defbc2acSJonathan Doman        or linux_config["turbo-ratio-limits-level-0"]
167*defbc2acSJonathan Doman    )
16894c94bfbSJonathan Doman    exp_turbo = []
16994c94bfbSJonathan Doman    for bucket_key in sorted(linux_turbo.keys()):
17094c94bfbSJonathan Doman        bucket = linux_turbo[bucket_key]
171f5a2d2adSPatrick Williams        exp_turbo.append(
172f5a2d2adSPatrick Williams            (
173f5a2d2adSPatrick Williams                int(bucket["core-count"]),
174f5a2d2adSPatrick Williams                int(bucket["max-turbo-frequency(MHz)"]),
175f5a2d2adSPatrick Williams            )
176f5a2d2adSPatrick Williams        )
17794c94bfbSJonathan Doman    compare(actual_turbo, exp_turbo, "SSE Turbo Profile")
17894c94bfbSJonathan Doman
17994c94bfbSJonathan Doman
18094c94bfbSJonathan Domandef get_level_from_config_id(config_id):
181f5a2d2adSPatrick Williams    match = re.match("config(\\d+)", config_id)
18294c94bfbSJonathan Doman    if not match:
18394c94bfbSJonathan Doman        raise RuntimeError(f"Invalid config name {config_id}")
18494c94bfbSJonathan Doman    return match.group(1)
18594c94bfbSJonathan Doman
18694c94bfbSJonathan Doman
18794c94bfbSJonathan Domandef main():
188f5a2d2adSPatrick Williams    parser = argparse.ArgumentParser(
189f5a2d2adSPatrick Williams        description="Compare Redfish SST properties against Linux tools"
190f5a2d2adSPatrick Williams    )
19194c94bfbSJonathan Doman    parser.add_argument("hostname")
19294c94bfbSJonathan Doman    parser.add_argument("--username", "-u", default="root")
19394c94bfbSJonathan Doman    parser.add_argument("--password", "-p", default="0penBmc")
19494c94bfbSJonathan Doman    args = parser.parse_args()
19594c94bfbSJonathan Doman
19694c94bfbSJonathan Doman    linux_data = get_linux_output()
19794c94bfbSJonathan Doman
198f5a2d2adSPatrick Williams    bmc = redfish.redfish_client(
199f5a2d2adSPatrick Williams        base_url=f"https://{args.hostname}",
200f5a2d2adSPatrick Williams        username=args.username,
201f5a2d2adSPatrick Williams        password=args.password,
202f5a2d2adSPatrick Williams    )
20394c94bfbSJonathan Doman    bmc.login(auth="session")
20494c94bfbSJonathan Doman
20594c94bfbSJonathan Doman    # Load the ProcessorCollection
20694c94bfbSJonathan Doman    resp = json.loads(bmc.get("/redfish/v1/Systems/system/Processors").text)
20794c94bfbSJonathan Doman    for proc_member in resp["Members"]:
20894c94bfbSJonathan Doman        proc_resp = json.loads(bmc.get(proc_member["@odata.id"]).text)
20994c94bfbSJonathan Doman        proc_id = proc_resp["Id"]
21094c94bfbSJonathan Doman        print()
21194c94bfbSJonathan Doman        print(f"----Checking Processor {proc_id}----")
21294c94bfbSJonathan Doman
21394c94bfbSJonathan Doman        if proc_resp["Status"]["State"] == "Absent":
21494c94bfbSJonathan Doman            print("Not populated")
21594c94bfbSJonathan Doman            continue
21694c94bfbSJonathan Doman
21794c94bfbSJonathan Doman        # Get subset of intel-speed-select data which applies to this CPU
21894c94bfbSJonathan Doman        pkg_data = get_linux_package(linux_data, proc_id)
21994c94bfbSJonathan Doman
22094c94bfbSJonathan Doman        # Check currently applied config
221f5a2d2adSPatrick Williams        applied_config = proc_resp["AppliedOperatingConfig"][
222f5a2d2adSPatrick Williams            "@odata.id"
223f5a2d2adSPatrick Williams        ].split("/")[-1]
22494c94bfbSJonathan Doman        current_level = get_level_from_config_id(applied_config)
225f5a2d2adSPatrick Williams        compare(
226f5a2d2adSPatrick Williams            current_level,
227f5a2d2adSPatrick Williams            pkg_data["get-config-current_level"],
228f5a2d2adSPatrick Williams            "Applied Config",
229f5a2d2adSPatrick Williams        )
23094c94bfbSJonathan Doman
23194c94bfbSJonathan Doman        exp_cur_level_data = pkg_data[f"perf-profile-level-{current_level}"]
23294c94bfbSJonathan Doman
23394c94bfbSJonathan Doman        # Check whether SST-BF is enabled
23494c94bfbSJonathan Doman        bf_enabled = proc_resp["BaseSpeedPriorityState"].lower()
23594c94bfbSJonathan Doman        exp_bf_enabled = exp_cur_level_data["speed-select-base-freq"]
23694c94bfbSJonathan Doman        if exp_bf_enabled == "unsupported":
23794c94bfbSJonathan Doman            exp_bf_enabled = "disabled"
23894c94bfbSJonathan Doman        compare(bf_enabled, exp_bf_enabled, "SST-BF Enabled?")
23994c94bfbSJonathan Doman
24094c94bfbSJonathan Doman        # Check high speed core list
24194c94bfbSJonathan Doman        hscores = set(proc_resp["HighSpeedCoreIDs"])
24294c94bfbSJonathan Doman        exp_hscores = set()
24394c94bfbSJonathan Doman        if "speed-select-base-freq-properties" in exp_cur_level_data:
244f5a2d2adSPatrick Williams            exp_hscores = exp_cur_level_data[
245f5a2d2adSPatrick Williams                "speed-select-base-freq-properties"
246f5a2d2adSPatrick Williams            ]["high-priority-cpu-list"]
247f5a2d2adSPatrick Williams            exp_hscores = set(
248f5a2d2adSPatrick Williams                [linux_cpu_map[int(x)] for x in exp_hscores.split(",")]
249f5a2d2adSPatrick Williams            )
25094c94bfbSJonathan Doman        compare(hscores, exp_hscores, "High Speed Core List")
25194c94bfbSJonathan Doman
25294c94bfbSJonathan Doman        # Load the OperatingConfigCollection
253f5a2d2adSPatrick Williams        resp = json.loads(
254f5a2d2adSPatrick Williams            bmc.get(proc_resp["OperatingConfigs"]["@odata.id"]).text
255f5a2d2adSPatrick Williams        )
25694c94bfbSJonathan Doman
25794c94bfbSJonathan Doman        # Check number of available configs
258f5a2d2adSPatrick Williams        profile_keys = list(
259f5a2d2adSPatrick Williams            filter(
260f5a2d2adSPatrick Williams                lambda x: x.startswith("perf-profile-level"), pkg_data.keys()
261f5a2d2adSPatrick Williams            )
262f5a2d2adSPatrick Williams        )
263f5a2d2adSPatrick Williams        compare(
264f5a2d2adSPatrick Williams            resp["Members@odata.count"],
265f5a2d2adSPatrick Williams            int(len(profile_keys)),
266f5a2d2adSPatrick Williams            "Number of profiles",
267f5a2d2adSPatrick Williams        )
26894c94bfbSJonathan Doman
26994c94bfbSJonathan Doman        for config_member in resp["Members"]:
27094c94bfbSJonathan Doman            # Load each OperatingConfig and compare all its contents
27194c94bfbSJonathan Doman            config_resp = json.loads(bmc.get(config_member["@odata.id"]).text)
27294c94bfbSJonathan Doman            level = get_level_from_config_id(config_resp["Id"])
27394c94bfbSJonathan Doman            exp_level_data = pkg_data[f"perf-profile-level-{level}"]
27494c94bfbSJonathan Doman            compare_config(config_resp, exp_level_data)
27594c94bfbSJonathan Doman
27694c94bfbSJonathan Doman    print()
27794c94bfbSJonathan Doman    if success:
27894c94bfbSJonathan Doman        print("Everything matched! :)")
27994c94bfbSJonathan Doman        return 0
28094c94bfbSJonathan Doman    else:
28194c94bfbSJonathan Doman        print("There were mismatches, please check output :(")
28294c94bfbSJonathan Doman        return 1
28394c94bfbSJonathan Doman
284f5a2d2adSPatrick Williams
28594c94bfbSJonathan Domanif __name__ == "__main__":
28694c94bfbSJonathan Doman    sys.exit(main())
287