amd-debug-tools 0.2.2__py3-none-any.whl → 0.2.12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- amd_debug/__init__.py +10 -3
- amd_debug/acpi.py +0 -1
- amd_debug/battery.py +0 -1
- amd_debug/bios.py +8 -5
- amd_debug/common.py +70 -3
- amd_debug/database.py +22 -5
- amd_debug/display.py +2 -3
- amd_debug/failures.py +44 -3
- amd_debug/installer.py +7 -6
- amd_debug/kernel.py +20 -17
- amd_debug/prerequisites.py +166 -61
- amd_debug/pstate.py +9 -6
- amd_debug/s2idle.py +53 -44
- amd_debug/sleep_report.py +129 -87
- amd_debug/templates/html +4 -2
- amd_debug/ttm.py +157 -0
- amd_debug/validator.py +61 -34
- amd_debug/wake.py +0 -1
- amd_debug_tools-0.2.12.dist-info/METADATA +75 -0
- amd_debug_tools-0.2.12.dist-info/RECORD +47 -0
- {amd_debug_tools-0.2.2.dist-info → amd_debug_tools-0.2.12.dist-info}/entry_points.txt +1 -0
- {amd_debug_tools-0.2.2.dist-info → amd_debug_tools-0.2.12.dist-info}/top_level.txt +1 -0
- launcher.py +0 -1
- test_acpi.py +1 -1
- test_bios.py +30 -12
- test_common.py +117 -1
- test_database.py +1 -1
- test_display.py +6 -6
- test_installer.py +68 -1
- test_kernel.py +7 -6
- test_launcher.py +9 -1
- test_prerequisites.py +629 -26
- test_s2idle.py +66 -19
- test_sleep_report.py +29 -0
- test_ttm.py +276 -0
- test_validator.py +187 -16
- amd_debug_tools-0.2.2.dist-info/METADATA +0 -181
- amd_debug_tools-0.2.2.dist-info/RECORD +0 -45
- {amd_debug_tools-0.2.2.dist-info → amd_debug_tools-0.2.12.dist-info}/WHEEL +0 -0
- {amd_debug_tools-0.2.2.dist-info → amd_debug_tools-0.2.12.dist-info}/licenses/LICENSE +0 -0
amd_debug/prerequisites.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/python3
|
|
2
1
|
# SPDX-License-Identifier: MIT
|
|
3
2
|
|
|
4
3
|
"""
|
|
@@ -21,7 +20,7 @@ import pyudev
|
|
|
21
20
|
|
|
22
21
|
from amd_debug.wake import WakeIRQ
|
|
23
22
|
from amd_debug.display import Display
|
|
24
|
-
from amd_debug.kernel import get_kernel_log, SystemdLogger, DmesgLogger
|
|
23
|
+
from amd_debug.kernel import get_kernel_log, SystemdLogger, CySystemdLogger, DmesgLogger
|
|
25
24
|
from amd_debug.common import (
|
|
26
25
|
apply_prefix_wrapper,
|
|
27
26
|
BIT,
|
|
@@ -55,12 +54,15 @@ from amd_debug.failures import (
|
|
|
55
54
|
I2CHidBug,
|
|
56
55
|
KernelRingBufferWrapped,
|
|
57
56
|
LimitedCores,
|
|
57
|
+
MissingAmdCaptureModule,
|
|
58
58
|
MissingAmdgpu,
|
|
59
59
|
MissingAmdgpuFirmware,
|
|
60
60
|
MissingAmdPmc,
|
|
61
|
+
MissingGpu,
|
|
61
62
|
MissingDriver,
|
|
62
63
|
MissingIommuACPI,
|
|
63
64
|
MissingIommuPolicy,
|
|
65
|
+
MissingIsp4PlatformDriver,
|
|
64
66
|
MissingThunderbolt,
|
|
65
67
|
MissingXhciHcd,
|
|
66
68
|
MSRFailure,
|
|
@@ -128,36 +130,56 @@ class PrerequisiteValidator(AmdTool):
|
|
|
128
130
|
if not self.db.get_last_prereq_ts():
|
|
129
131
|
self.run()
|
|
130
132
|
|
|
133
|
+
def capture_nvidia(self):
|
|
134
|
+
"""Capture the NVIDIA GPU state"""
|
|
135
|
+
p = os.path.join("/", "proc", "driver", "nvidia", "version")
|
|
136
|
+
if not os.path.exists(p):
|
|
137
|
+
return True
|
|
138
|
+
try:
|
|
139
|
+
self.db.record_debug_file(p)
|
|
140
|
+
except PermissionError:
|
|
141
|
+
self.db.record_prereq("NVIDIA GPU version not readable", "👀")
|
|
142
|
+
return True
|
|
143
|
+
p = os.path.join("/", "proc", "driver", "nvidia", "gpus")
|
|
144
|
+
if not os.path.exists(p):
|
|
145
|
+
return True
|
|
146
|
+
for root, _dirs, files in os.walk(p, topdown=False):
|
|
147
|
+
for f in files:
|
|
148
|
+
try:
|
|
149
|
+
self.db.record_debug(f"NVIDIA {f}")
|
|
150
|
+
self.db.record_debug_file(os.path.join(root, f))
|
|
151
|
+
except PermissionError:
|
|
152
|
+
self.db.record_prereq("NVIDIA GPU {f} not readable", "👀")
|
|
153
|
+
return True
|
|
154
|
+
|
|
131
155
|
def capture_edid(self):
|
|
132
156
|
"""Capture and decode the EDID data"""
|
|
133
157
|
edids = self.display.get_edid()
|
|
134
158
|
if len(edids) == 0:
|
|
135
159
|
self.db.record_debug("No EDID data found")
|
|
136
160
|
return True
|
|
137
|
-
|
|
138
|
-
for name, p in edids.items():
|
|
161
|
+
for p in edids:
|
|
139
162
|
output = None
|
|
140
|
-
for
|
|
163
|
+
for tool in ["di-edid-decode", "edid-decode"]:
|
|
141
164
|
try:
|
|
142
|
-
cmd = [
|
|
165
|
+
cmd = [tool, p]
|
|
143
166
|
output = subprocess.check_output(
|
|
144
167
|
cmd, stderr=subprocess.DEVNULL
|
|
145
|
-
).decode("utf-8")
|
|
168
|
+
).decode("utf-8", errors="ignore")
|
|
146
169
|
break
|
|
147
170
|
except FileNotFoundError:
|
|
148
171
|
self.db.record_debug(f"{cmd} not installed")
|
|
149
|
-
except subprocess.CalledProcessError as
|
|
150
|
-
|
|
151
|
-
f"failed to capture edid with {cmd}: {e.output}"
|
|
152
|
-
)
|
|
172
|
+
except subprocess.CalledProcessError as _e:
|
|
173
|
+
pass
|
|
153
174
|
if not output:
|
|
154
175
|
self.db.record_prereq("Failed to capture EDID table", "👀")
|
|
155
176
|
else:
|
|
156
|
-
self.db.record_debug(apply_prefix_wrapper(f"EDID for {
|
|
177
|
+
self.db.record_debug(apply_prefix_wrapper(f"EDID for {p}:", output))
|
|
157
178
|
return True
|
|
158
179
|
|
|
159
180
|
def check_amdgpu(self):
|
|
160
181
|
"""Check for the AMDGPU driver"""
|
|
182
|
+
count = 0
|
|
161
183
|
for device in self.pyudev.list_devices(subsystem="pci"):
|
|
162
184
|
klass = device.properties.get("PCI_CLASS")
|
|
163
185
|
if klass not in ["30000", "38000"]:
|
|
@@ -165,6 +187,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
165
187
|
pci_id = device.properties.get("PCI_ID")
|
|
166
188
|
if not pci_id.startswith("1002"):
|
|
167
189
|
continue
|
|
190
|
+
count += 1
|
|
168
191
|
if device.properties.get("DRIVER") != "amdgpu":
|
|
169
192
|
self.db.record_prereq("GPU driver `amdgpu` not loaded", "❌")
|
|
170
193
|
self.failures += [MissingAmdgpu()]
|
|
@@ -172,6 +195,10 @@ class PrerequisiteValidator(AmdTool):
|
|
|
172
195
|
slot = device.properties.get("PCI_SLOT_NAME")
|
|
173
196
|
|
|
174
197
|
self.db.record_prereq(f"GPU driver `amdgpu` bound to {slot}", "✅")
|
|
198
|
+
if count == 0:
|
|
199
|
+
self.db.record_prereq("Integrated GPU not found", "❌")
|
|
200
|
+
self.failures += [MissingGpu()]
|
|
201
|
+
return False
|
|
175
202
|
return True
|
|
176
203
|
|
|
177
204
|
def check_amdgpu_parameters(self):
|
|
@@ -245,7 +272,6 @@ class PrerequisiteValidator(AmdTool):
|
|
|
245
272
|
for dev in self.pyudev.list_devices(subsystem="pci", DRIVER="nvme"):
|
|
246
273
|
# https://git.kernel.org/torvalds/c/e79a10652bbd3
|
|
247
274
|
if minimum_kernel(6, 10):
|
|
248
|
-
self.db.record_debug("New enough kernel to avoid NVME check")
|
|
249
275
|
break
|
|
250
276
|
pci_slot_name = dev.properties["PCI_SLOT_NAME"]
|
|
251
277
|
vendor = dev.properties.get("ID_VENDOR_FROM_DATABASE", "")
|
|
@@ -319,7 +345,6 @@ class PrerequisiteValidator(AmdTool):
|
|
|
319
345
|
# not needed to check in newer kernels
|
|
320
346
|
# see https://github.com/torvalds/linux/commit/77f1972bdcf7513293e8bbe376b9fe837310ee9c
|
|
321
347
|
if minimum_kernel(6, 10):
|
|
322
|
-
self.db.record_debug("New enough kernel to avoid HSMP check")
|
|
323
348
|
return True
|
|
324
349
|
f = os.path.join("/", "boot", f"config-{platform.uname().release}")
|
|
325
350
|
if os.path.exists(f):
|
|
@@ -527,7 +552,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
527
552
|
f"{keys['sys_vendor']} {keys['product_name']} ({keys['product_family']})",
|
|
528
553
|
"💻",
|
|
529
554
|
)
|
|
530
|
-
debug_str = "DMI
|
|
555
|
+
debug_str = "DMI|value\n"
|
|
531
556
|
for key, value in keys.items():
|
|
532
557
|
if (
|
|
533
558
|
"product_name" in key
|
|
@@ -535,7 +560,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
535
560
|
or "product_family" in key
|
|
536
561
|
):
|
|
537
562
|
continue
|
|
538
|
-
debug_str += f"{key}
|
|
563
|
+
debug_str += f"{key}| {value}\n"
|
|
539
564
|
self.db.record_debug(debug_str)
|
|
540
565
|
return True
|
|
541
566
|
|
|
@@ -600,6 +625,9 @@ class PrerequisiteValidator(AmdTool):
|
|
|
600
625
|
with open(target, "rb") as r:
|
|
601
626
|
r.seek(0x70)
|
|
602
627
|
found = struct.unpack("<I", r.read(4))[0] & BIT(21)
|
|
628
|
+
except FileNotFoundError:
|
|
629
|
+
self.db.record_prereq("FADT check unavailable", "🚦")
|
|
630
|
+
return True
|
|
603
631
|
except PermissionError:
|
|
604
632
|
self.db.record_prereq("FADT check unavailable", "🚦")
|
|
605
633
|
return True
|
|
@@ -652,7 +680,9 @@ class PrerequisiteValidator(AmdTool):
|
|
|
652
680
|
"""Check the source for kernel logs"""
|
|
653
681
|
if isinstance(self.kernel_log, SystemdLogger):
|
|
654
682
|
self.db.record_prereq("Logs are provided via systemd", "✅")
|
|
655
|
-
|
|
683
|
+
elif isinstance(self.kernel_log, CySystemdLogger):
|
|
684
|
+
self.db.record_prereq("Logs are provided via cysystemd", "✅")
|
|
685
|
+
elif isinstance(self.kernel_log, DmesgLogger):
|
|
656
686
|
self.db.record_prereq(
|
|
657
687
|
"Logs are provided via dmesg, timestamps may not be accurate over multiple cycles",
|
|
658
688
|
"🚦",
|
|
@@ -720,6 +750,9 @@ class PrerequisiteValidator(AmdTool):
|
|
|
720
750
|
p = os.path.join("/", "sys", "kernel", "debug", "gpio")
|
|
721
751
|
try:
|
|
722
752
|
contents = read_file(p)
|
|
753
|
+
except FileNotFoundError:
|
|
754
|
+
self.db.record_prereq("GPIO debugfs not available", "👀")
|
|
755
|
+
contents = None
|
|
723
756
|
except PermissionError:
|
|
724
757
|
self.db.record_debug(f"Unable to capture {p}")
|
|
725
758
|
contents = None
|
|
@@ -756,7 +789,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
756
789
|
"utf-8"
|
|
757
790
|
)
|
|
758
791
|
except FileNotFoundError:
|
|
759
|
-
self.db.record_prereq(
|
|
792
|
+
self.db.record_prereq("ethtool is missing", "👀")
|
|
760
793
|
return True
|
|
761
794
|
for line in output.split("\n"):
|
|
762
795
|
if "Supports Wake-on" in line:
|
|
@@ -916,7 +949,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
916
949
|
devices = []
|
|
917
950
|
for dev in self.pyudev.list_devices(subsystem="pci"):
|
|
918
951
|
devices.append(dev)
|
|
919
|
-
debug_str = "PCI
|
|
952
|
+
debug_str = "PCI Slot | Vendor | Class | ID | ACPI path\n"
|
|
920
953
|
for dev in devices:
|
|
921
954
|
pci_id = dev.properties["PCI_ID"].lower()
|
|
922
955
|
pci_slot_name = dev.properties["PCI_SLOT_NAME"]
|
|
@@ -939,18 +972,53 @@ class PrerequisiteValidator(AmdTool):
|
|
|
939
972
|
p = os.path.join(dev.sys_path, "firmware_node", "path")
|
|
940
973
|
if os.path.exists(p):
|
|
941
974
|
acpi = read_file(p)
|
|
942
|
-
debug_str += (
|
|
943
|
-
f"{prefix}{pci_slot_name} : "
|
|
944
|
-
f"{database_vendor} {database_class} [{pci_id}] : {acpi}\n"
|
|
945
|
-
)
|
|
946
975
|
else:
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
976
|
+
acpi = ""
|
|
977
|
+
debug_str += (
|
|
978
|
+
f"{prefix}{pci_slot_name} | "
|
|
979
|
+
f"{database_vendor} | {database_class} | {pci_id} | {acpi}\n"
|
|
980
|
+
)
|
|
951
981
|
if debug_str:
|
|
952
982
|
self.db.record_debug(debug_str)
|
|
953
983
|
|
|
984
|
+
def check_isp4(self):
|
|
985
|
+
"""Check if camera supported by ISP is present and set up properly"""
|
|
986
|
+
devices = []
|
|
987
|
+
for dev in self.pyudev.list_devices(subsystem="acpi"):
|
|
988
|
+
# look for ACPI device for camera (OMNI5C10:00 is seen today)
|
|
989
|
+
if not dev.sys_name.startswith("OMNI5C10"):
|
|
990
|
+
continue
|
|
991
|
+
p = os.path.join(dev.sys_path, "path")
|
|
992
|
+
if not os.path.exists(p):
|
|
993
|
+
continue
|
|
994
|
+
devices.append(dev)
|
|
995
|
+
for dev in devices:
|
|
996
|
+
p = os.path.join(dev.sys_path, "physical_node", "driver")
|
|
997
|
+
if os.path.exists(p):
|
|
998
|
+
driver = os.path.basename(os.readlink(p))
|
|
999
|
+
else:
|
|
1000
|
+
driver = None
|
|
1001
|
+
if driver != "amd-isp4":
|
|
1002
|
+
self.db.record_prereq(
|
|
1003
|
+
f"ISP4 platform camera driver 'amd-isp4' not bound to {dev.sys_name}",
|
|
1004
|
+
"❌",
|
|
1005
|
+
)
|
|
1006
|
+
self.failures += [MissingIsp4PlatformDriver()]
|
|
1007
|
+
return False
|
|
1008
|
+
self.db.record_prereq(
|
|
1009
|
+
f"ISP4 platform camera driver 'amd-isp4' bound to {dev.sys_name}", "✅"
|
|
1010
|
+
)
|
|
1011
|
+
# check if `amd_capture` module is loaded
|
|
1012
|
+
p = os.path.join("/", "sys", "module", "amd_capture")
|
|
1013
|
+
if not os.path.exists(p):
|
|
1014
|
+
self.db.record_prereq(
|
|
1015
|
+
"Camera driver module 'amd_capture' not loaded", "❌"
|
|
1016
|
+
)
|
|
1017
|
+
self.failures += [MissingAmdCaptureModule()]
|
|
1018
|
+
return False
|
|
1019
|
+
self.db.record_prereq("Camera driver module 'amd_capture' loaded", "✅")
|
|
1020
|
+
return True
|
|
1021
|
+
|
|
954
1022
|
def map_acpi_path(self):
|
|
955
1023
|
"""Map of ACPI devices to ACPI paths"""
|
|
956
1024
|
devices = []
|
|
@@ -964,12 +1032,8 @@ class PrerequisiteValidator(AmdTool):
|
|
|
964
1032
|
if status == 0:
|
|
965
1033
|
continue
|
|
966
1034
|
devices.append(dev)
|
|
967
|
-
debug_str = "ACPI name
|
|
1035
|
+
debug_str = "ACPI name | ACPI path | Kernel driver\n"
|
|
968
1036
|
for dev in devices:
|
|
969
|
-
if dev == devices[-1]:
|
|
970
|
-
prefix = "└─"
|
|
971
|
-
else:
|
|
972
|
-
prefix = "│ "
|
|
973
1037
|
p = os.path.join(dev.sys_path, "path")
|
|
974
1038
|
pth = read_file(p)
|
|
975
1039
|
p = os.path.join(dev.sys_path, "physical_node", "driver")
|
|
@@ -977,7 +1041,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
977
1041
|
driver = os.path.basename(os.readlink(p))
|
|
978
1042
|
else:
|
|
979
1043
|
driver = None
|
|
980
|
-
debug_str += f"{
|
|
1044
|
+
debug_str += f"{dev.sys_name} | {pth} | {driver}\n"
|
|
981
1045
|
if debug_str:
|
|
982
1046
|
self.db.record_debug(debug_str)
|
|
983
1047
|
return True
|
|
@@ -1016,6 +1080,21 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1016
1080
|
shutil.rmtree(tmpd)
|
|
1017
1081
|
return True
|
|
1018
1082
|
|
|
1083
|
+
def capture_cstates(self):
|
|
1084
|
+
"""Capture ACPI C state information for the first CPU (assumes the same for all CPUs)"""
|
|
1085
|
+
base = os.path.join("/", "sys", "bus", "cpu", "devices", "cpu0", "cpuidle")
|
|
1086
|
+
paths = {}
|
|
1087
|
+
for root, _dirs, files in os.walk(base, topdown=False):
|
|
1088
|
+
for fname in files:
|
|
1089
|
+
target = os.path.join(root, fname)
|
|
1090
|
+
with open(target, "rb") as f:
|
|
1091
|
+
paths[target] = f.read()
|
|
1092
|
+
debug_str = "ACPI C-state information\n"
|
|
1093
|
+
for path, data in paths.items():
|
|
1094
|
+
prefix = "│ " if path != list(paths.keys())[-1] else "└─"
|
|
1095
|
+
debug_str += f"{prefix}{path}: {data.decode('utf-8', 'ignore')}"
|
|
1096
|
+
self.db.record_debug(debug_str)
|
|
1097
|
+
|
|
1019
1098
|
def capture_battery(self):
|
|
1020
1099
|
"""Capture battery information"""
|
|
1021
1100
|
obj = Batteries()
|
|
@@ -1074,11 +1153,22 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1074
1153
|
# check for artificially limited CPUs
|
|
1075
1154
|
p = os.path.join("/", "sys", "devices", "system", "cpu", "kernel_max")
|
|
1076
1155
|
max_cpus = int(read_file(p)) + 1 # 0 indexed
|
|
1077
|
-
# https://
|
|
1078
|
-
# Extended Topology
|
|
1079
|
-
# CPUID 0x80000026 subleaf 1
|
|
1156
|
+
# https://docs.amd.com/v/u/en-US/40332-PUB_4.08
|
|
1157
|
+
# E.4.24 Function 8000_0026—Extended CPU Topology
|
|
1080
1158
|
try:
|
|
1081
|
-
|
|
1159
|
+
# Need to discover the socket level to look at all CCDs in the socket
|
|
1160
|
+
for level in range(0, 5):
|
|
1161
|
+
_, _, ecx, _ = read_cpuid(0, 0x80000026, level)
|
|
1162
|
+
level_type = (ecx >> 8) & 0xFF
|
|
1163
|
+
if level_type == 0:
|
|
1164
|
+
self.db.record_prereq(
|
|
1165
|
+
"Unable to discover CPU topology, didn't find socket level",
|
|
1166
|
+
"🚦",
|
|
1167
|
+
)
|
|
1168
|
+
return True
|
|
1169
|
+
if level_type == 4:
|
|
1170
|
+
break
|
|
1171
|
+
_, cpu_count, _, _ = read_cpuid(0, 0x80000026, level)
|
|
1082
1172
|
if cpu_count > max_cpus:
|
|
1083
1173
|
self.db.record_prereq(
|
|
1084
1174
|
f"The kernel has been limited to {max_cpus} CPU cores, "
|
|
@@ -1149,32 +1239,17 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1149
1239
|
if self.cpu_family == 0x1A and self.cpu_model in affected_1a:
|
|
1150
1240
|
found_iommu = False
|
|
1151
1241
|
found_acpi = False
|
|
1152
|
-
found_dmar = False
|
|
1153
1242
|
for dev in self.pyudev.list_devices(subsystem="iommu"):
|
|
1154
1243
|
found_iommu = True
|
|
1155
1244
|
debug_str += f"Found IOMMU {dev.sys_path}\n"
|
|
1156
1245
|
break
|
|
1246
|
+
|
|
1247
|
+
# User turned off IOMMU, no problems
|
|
1157
1248
|
if not found_iommu:
|
|
1158
1249
|
self.db.record_prereq("IOMMU disabled", "✅")
|
|
1159
1250
|
return True
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
with open(p, "rb") as f:
|
|
1163
|
-
data = f.read()
|
|
1164
|
-
if len(data) < 40:
|
|
1165
|
-
raise ValueError(
|
|
1166
|
-
"IVRS table appears too small to contain virtualization info."
|
|
1167
|
-
)
|
|
1168
|
-
virt_info = struct.unpack_from("I", data, 36)[0]
|
|
1169
|
-
debug_str += f"Virtualization info: 0x{virt_info:x}"
|
|
1170
|
-
found_dmar = (virt_info & 0x2) != 0
|
|
1171
|
-
self.db.record_debug(debug_str)
|
|
1172
|
-
if not found_dmar:
|
|
1173
|
-
self.db.record_prereq(
|
|
1174
|
-
"IOMMU is misconfigured: Pre-boot DMA protection not enabled", "❌"
|
|
1175
|
-
)
|
|
1176
|
-
self.failures += [DMArNotEnabled()]
|
|
1177
|
-
return False
|
|
1251
|
+
|
|
1252
|
+
# Look for MSFT0201 in DSDT/SSDT
|
|
1178
1253
|
for dev in self.pyudev.list_devices(subsystem="acpi"):
|
|
1179
1254
|
if "MSFT0201" in dev.sys_path:
|
|
1180
1255
|
found_acpi = True
|
|
@@ -1184,13 +1259,39 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1184
1259
|
)
|
|
1185
1260
|
self.failures += [MissingIommuACPI("MSFT0201")]
|
|
1186
1261
|
return False
|
|
1187
|
-
|
|
1262
|
+
|
|
1263
|
+
# Check that policy is bound to it
|
|
1188
1264
|
for dev in self.pyudev.list_devices(subsystem="platform"):
|
|
1189
1265
|
if "MSFT0201" in dev.sys_path:
|
|
1190
1266
|
p = os.path.join(dev.sys_path, "iommu")
|
|
1191
1267
|
if not os.path.exists(p):
|
|
1192
1268
|
self.failures += [MissingIommuPolicy("MSFT0201")]
|
|
1193
1269
|
return False
|
|
1270
|
+
|
|
1271
|
+
# Check pre-boot DMA
|
|
1272
|
+
p = os.path.join("/", "sys", "firmware", "acpi", "tables", "IVRS")
|
|
1273
|
+
with open(p, "rb") as f:
|
|
1274
|
+
data = f.read()
|
|
1275
|
+
if len(data) < 40:
|
|
1276
|
+
raise ValueError(
|
|
1277
|
+
"IVRS table appears too small to contain virtualization info."
|
|
1278
|
+
)
|
|
1279
|
+
virt_info = struct.unpack_from("I", data, 36)[0]
|
|
1280
|
+
debug_str += f"IVRS: Virtualization info: 0x{virt_info:x}\n"
|
|
1281
|
+
found_ivrs_dmar = (virt_info & 0x2) != 0
|
|
1282
|
+
|
|
1283
|
+
# check for MSFT0201 in IVRS (alternative to pre-boot DMA)
|
|
1284
|
+
target_bytes = "MSFT0201".encode("utf-8")
|
|
1285
|
+
found_ivrs_msft0201 = data.find(target_bytes) != -1
|
|
1286
|
+
debug_str += f"IVRS: Found MSFT0201: {found_ivrs_msft0201}"
|
|
1287
|
+
|
|
1288
|
+
self.db.record_debug(debug_str)
|
|
1289
|
+
if not found_ivrs_dmar and not found_ivrs_msft0201:
|
|
1290
|
+
self.db.record_prereq(
|
|
1291
|
+
"IOMMU is misconfigured: Pre-boot DMA protection not enabled", "❌"
|
|
1292
|
+
)
|
|
1293
|
+
self.failures += [DMArNotEnabled()]
|
|
1294
|
+
return False
|
|
1194
1295
|
self.db.record_prereq("IOMMU properly configured", "✅")
|
|
1195
1296
|
return True
|
|
1196
1297
|
|
|
@@ -1200,6 +1301,8 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1200
1301
|
return True
|
|
1201
1302
|
if self.cpu_model not in [0x74, 0x78]:
|
|
1202
1303
|
return True
|
|
1304
|
+
if not self.smu_version:
|
|
1305
|
+
return True
|
|
1203
1306
|
if version.parse(self.smu_version) > version.parse("76.60.0"):
|
|
1204
1307
|
return True
|
|
1205
1308
|
if version.parse(self.smu_version) < version.parse("76.18.0"):
|
|
@@ -1222,9 +1325,8 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1222
1325
|
# ignore kernel warnings
|
|
1223
1326
|
taint &= ~BIT(9)
|
|
1224
1327
|
if taint != 0:
|
|
1225
|
-
self.db.record_prereq(f"Kernel is tainted: {taint}", "
|
|
1328
|
+
self.db.record_prereq(f"Kernel is tainted: {taint}", "🚦")
|
|
1226
1329
|
self.failures += [TaintedKernel()]
|
|
1227
|
-
return False
|
|
1228
1330
|
return True
|
|
1229
1331
|
|
|
1230
1332
|
def run(self):
|
|
@@ -1239,6 +1341,8 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1239
1341
|
self.capture_logind,
|
|
1240
1342
|
self.capture_pci_acpi,
|
|
1241
1343
|
self.capture_edid,
|
|
1344
|
+
self.capture_nvidia,
|
|
1345
|
+
self.capture_cstates,
|
|
1242
1346
|
]
|
|
1243
1347
|
checks = []
|
|
1244
1348
|
|
|
@@ -1268,6 +1372,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1268
1372
|
self.check_iommu,
|
|
1269
1373
|
self.check_asus_rog_ally,
|
|
1270
1374
|
self.check_dpia_pg_dmcub,
|
|
1375
|
+
self.check_isp4,
|
|
1271
1376
|
]
|
|
1272
1377
|
|
|
1273
1378
|
checks += [
|
|
@@ -1291,7 +1396,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1291
1396
|
if not check():
|
|
1292
1397
|
result = False
|
|
1293
1398
|
if not result:
|
|
1294
|
-
self.db.record_prereq(Headers.BrokenPrerequisites, "
|
|
1399
|
+
self.db.record_prereq(Headers.BrokenPrerequisites, "🚫")
|
|
1295
1400
|
self.db.sync()
|
|
1296
1401
|
clear_temporary_message(len(msg))
|
|
1297
1402
|
return result
|
|
@@ -1310,7 +1415,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1310
1415
|
logging.debug(line)
|
|
1311
1416
|
|
|
1312
1417
|
if len(self.failures) == 0:
|
|
1313
|
-
return
|
|
1418
|
+
return
|
|
1314
1419
|
print_color(Headers.ExplanationReport, "🗣️")
|
|
1315
1420
|
for item in self.failures:
|
|
1316
1421
|
item.get_failure()
|
amd_debug/pstate.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/python3
|
|
2
1
|
# SPDX-License-Identifier: MIT
|
|
3
2
|
"""CPPC triage script for AMD systems"""
|
|
4
3
|
|
|
@@ -293,22 +292,26 @@ def parse_args():
|
|
|
293
292
|
action="store_true",
|
|
294
293
|
help="Enable tool debug logging",
|
|
295
294
|
)
|
|
296
|
-
|
|
295
|
+
parser.add_argument(
|
|
296
|
+
"--version", action="store_true", help="Show version information"
|
|
297
|
+
)
|
|
297
298
|
if len(sys.argv) == 1:
|
|
298
299
|
parser.print_help(sys.stderr)
|
|
299
300
|
sys.exit(1)
|
|
300
301
|
return parser.parse_args()
|
|
301
302
|
|
|
302
303
|
|
|
303
|
-
def main():
|
|
304
|
+
def main() -> None | int:
|
|
304
305
|
"""Main function"""
|
|
305
306
|
args = parse_args()
|
|
306
307
|
ret = False
|
|
307
|
-
if args.
|
|
308
|
+
if args.version:
|
|
308
309
|
print(version())
|
|
309
|
-
return
|
|
310
|
+
return
|
|
310
311
|
elif args.command == "triage":
|
|
311
312
|
triage = AmdPstateTriage(args.tool_debug)
|
|
312
313
|
ret = triage.run()
|
|
313
314
|
show_log_info()
|
|
314
|
-
|
|
315
|
+
if ret is False:
|
|
316
|
+
return 1
|
|
317
|
+
return
|
amd_debug/s2idle.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/python3
|
|
2
1
|
# SPDX-License-Identifier: MIT
|
|
3
2
|
"""s2idle analysis tool"""
|
|
4
3
|
import argparse
|
|
@@ -11,6 +10,7 @@ from datetime import date, timedelta, datetime
|
|
|
11
10
|
from amd_debug.common import (
|
|
12
11
|
convert_string_to_bool,
|
|
13
12
|
colorize_choices,
|
|
13
|
+
fatal_error,
|
|
14
14
|
is_root,
|
|
15
15
|
relaunch_sudo,
|
|
16
16
|
show_log_info,
|
|
@@ -61,21 +61,22 @@ def display_report_file(fname, fmt) -> None:
|
|
|
61
61
|
return
|
|
62
62
|
user = os.environ.get("SUDO_USER")
|
|
63
63
|
if user:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
cmd = [
|
|
65
|
+
"systemd-run",
|
|
66
|
+
"--user",
|
|
67
|
+
f"--machine={user}@.host",
|
|
68
|
+
"xdg-open",
|
|
69
|
+
os.path.abspath(fname),
|
|
70
|
+
]
|
|
71
|
+
ret = subprocess.call(cmd)
|
|
72
|
+
if ret:
|
|
73
|
+
fatal_error(f"Failed to open report: {ret}")
|
|
73
74
|
|
|
74
75
|
|
|
75
76
|
def get_report_file(report_file, extension) -> str:
|
|
76
77
|
"""Prompt user for report file"""
|
|
77
78
|
if extension == "stdout":
|
|
78
|
-
return
|
|
79
|
+
return ""
|
|
79
80
|
if not report_file:
|
|
80
81
|
return f"amd-s2idle-report-{date.today()}.{extension}"
|
|
81
82
|
return report_file
|
|
@@ -88,7 +89,7 @@ def get_report_format() -> str:
|
|
|
88
89
|
return "html"
|
|
89
90
|
|
|
90
91
|
|
|
91
|
-
def prompt_report_arguments(since, until, fname, fmt, report_debug) ->
|
|
92
|
+
def prompt_report_arguments(since, until, fname, fmt, report_debug) -> list:
|
|
92
93
|
"""Prompt user for report configuration"""
|
|
93
94
|
if not since:
|
|
94
95
|
default = Defaults.since
|
|
@@ -120,7 +121,7 @@ def prompt_report_arguments(since, until, fname, fmt, report_debug) -> str:
|
|
|
120
121
|
if report_debug is None:
|
|
121
122
|
inp = (
|
|
122
123
|
input(
|
|
123
|
-
f"{Headers.ReportDebugDescription} ({colorize_choices(Defaults.boolean_choices,
|
|
124
|
+
f"{Headers.ReportDebugDescription} ({colorize_choices(Defaults.boolean_choices, 'true')})? "
|
|
124
125
|
)
|
|
125
126
|
.lower()
|
|
126
127
|
.capitalize()
|
|
@@ -211,6 +212,16 @@ def run_test_cycle(
|
|
|
211
212
|
print("Failed to install dependencies")
|
|
212
213
|
return False
|
|
213
214
|
|
|
215
|
+
try:
|
|
216
|
+
duration, wait, count = prompt_test_arguments(duration, wait, count, rand)
|
|
217
|
+
total_seconds = (duration + wait) * count
|
|
218
|
+
until_time = datetime.now() + timedelta(seconds=total_seconds)
|
|
219
|
+
since, until, fname, fmt, report_debug = prompt_report_arguments(
|
|
220
|
+
datetime.now().isoformat(), until_time.isoformat(), fname, fmt, True
|
|
221
|
+
)
|
|
222
|
+
except KeyboardInterrupt:
|
|
223
|
+
sys.exit("\nTest cancelled")
|
|
224
|
+
|
|
214
225
|
try:
|
|
215
226
|
app = PrerequisiteValidator(debug)
|
|
216
227
|
run = app.run()
|
|
@@ -221,37 +232,33 @@ def run_test_cycle(
|
|
|
221
232
|
|
|
222
233
|
if run or force:
|
|
223
234
|
app = SleepValidator(tool_debug=debug, bios_debug=bios_debug)
|
|
224
|
-
try:
|
|
225
|
-
duration, wait, count = prompt_test_arguments(duration, wait, count, rand)
|
|
226
|
-
since, until, fname, fmt, report_debug = prompt_report_arguments(
|
|
227
|
-
datetime.now().isoformat(), Defaults.until.isoformat(), fname, fmt, True
|
|
228
|
-
)
|
|
229
|
-
except KeyboardInterrupt:
|
|
230
|
-
sys.exit("\nTest cancelled")
|
|
231
235
|
|
|
232
|
-
app.run(
|
|
236
|
+
run = app.run(
|
|
233
237
|
duration=duration,
|
|
234
238
|
wait=wait,
|
|
235
239
|
count=count,
|
|
236
240
|
rand=rand,
|
|
237
241
|
logind=logind,
|
|
238
242
|
)
|
|
243
|
+
until = datetime.now()
|
|
244
|
+
else:
|
|
245
|
+
since = None
|
|
246
|
+
until = None
|
|
247
|
+
|
|
248
|
+
app = SleepReport(
|
|
249
|
+
since=since,
|
|
250
|
+
until=until,
|
|
251
|
+
fname=fname,
|
|
252
|
+
fmt=fmt,
|
|
253
|
+
tool_debug=debug,
|
|
254
|
+
report_debug=report_debug,
|
|
255
|
+
)
|
|
256
|
+
app.run()
|
|
239
257
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
until=until,
|
|
243
|
-
fname=fname,
|
|
244
|
-
fmt=fmt,
|
|
245
|
-
tool_debug=debug,
|
|
246
|
-
report_debug=report_debug,
|
|
247
|
-
)
|
|
248
|
-
app.run()
|
|
249
|
-
|
|
250
|
-
# open report in browser if it's html
|
|
251
|
-
display_report_file(fname, fmt)
|
|
258
|
+
# open report in browser if it's html
|
|
259
|
+
display_report_file(fname, fmt)
|
|
252
260
|
|
|
253
|
-
|
|
254
|
-
return False
|
|
261
|
+
return True
|
|
255
262
|
|
|
256
263
|
|
|
257
264
|
def install(debug) -> None:
|
|
@@ -308,10 +315,8 @@ def parse_args():
|
|
|
308
315
|
test_cmd.add_argument(
|
|
309
316
|
"--random",
|
|
310
317
|
action="store_true",
|
|
311
|
-
help=
|
|
312
|
-
|
|
313
|
-
"--duration and --wait arguments as an upper bound",
|
|
314
|
-
),
|
|
318
|
+
help="Run sleep cycles for random durations and wait, using the "
|
|
319
|
+
"--duration and --wait arguments as an upper bound",
|
|
315
320
|
)
|
|
316
321
|
test_cmd.add_argument(
|
|
317
322
|
"--force",
|
|
@@ -384,7 +389,9 @@ def parse_args():
|
|
|
384
389
|
help="Enable tool debug logging",
|
|
385
390
|
)
|
|
386
391
|
|
|
387
|
-
|
|
392
|
+
parser.add_argument(
|
|
393
|
+
"--version", action="store_true", help="Show version information"
|
|
394
|
+
)
|
|
388
395
|
|
|
389
396
|
if len(sys.argv) == 1:
|
|
390
397
|
parser.print_help(sys.stderr)
|
|
@@ -393,7 +400,7 @@ def parse_args():
|
|
|
393
400
|
return parser.parse_args()
|
|
394
401
|
|
|
395
402
|
|
|
396
|
-
def main():
|
|
403
|
+
def main() -> None | int:
|
|
397
404
|
"""Main function"""
|
|
398
405
|
args = parse_args()
|
|
399
406
|
ret = False
|
|
@@ -426,10 +433,12 @@ def main():
|
|
|
426
433
|
args.logind,
|
|
427
434
|
args.bios_debug,
|
|
428
435
|
)
|
|
429
|
-
elif args.
|
|
436
|
+
elif args.version:
|
|
430
437
|
print(version())
|
|
431
|
-
return
|
|
438
|
+
return
|
|
432
439
|
else:
|
|
433
440
|
sys.exit("no action specified")
|
|
434
441
|
show_log_info()
|
|
435
|
-
|
|
442
|
+
if ret is False:
|
|
443
|
+
return 1
|
|
444
|
+
return
|