amd-debug-tools 0.2.1__tar.gz → 0.2.3__tar.gz
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.
Potentially problematic release.
This version of amd-debug-tools might be problematic. Click here for more details.
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/PKG-INFO +4 -3
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/README.md +3 -2
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/common.py +24 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/failures.py +14 -2
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/installer.py +34 -2
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/kernel.py +8 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/prerequisites.py +95 -45
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/s2idle.py +45 -30
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/sleep_report.py +128 -85
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/templates/html +4 -2
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/templates/md +0 -7
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/validator.py +20 -15
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug_tools.egg-info/PKG-INFO +4 -3
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/test_common.py +112 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/test_installer.py +8 -6
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/test_prerequisites.py +204 -45
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/test_s2idle.py +93 -28
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/test_sleep_report.py +29 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/test_validator.py +119 -12
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/LICENSE +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/pyproject.toml +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/setup.cfg +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/__init__.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/acpi.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/bash/amd-s2idle +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/battery.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/bios.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/database.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/display.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/pstate.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/s2idle-hook +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/templates/stdout +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/templates/txt +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug/wake.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug_tools.egg-info/SOURCES.txt +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug_tools.egg-info/dependency_links.txt +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug_tools.egg-info/entry_points.txt +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug_tools.egg-info/requires.txt +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/amd_debug_tools.egg-info/top_level.txt +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/launcher.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/test_acpi.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/test_batteries.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/test_bios.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/test_database.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/test_display.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/test_failures.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/test_kernel.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/test_launcher.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/test_pstate.py +0 -0
- {amd_debug_tools-0.2.1 → amd_debug_tools-0.2.3}/src/test_wake.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: amd-debug-tools
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: debug tools for AMD systems
|
|
5
5
|
Author-email: Mario Limonciello <superm1@kernel.org>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -125,8 +125,9 @@ The following optional arguments are supported for this command:
|
|
|
125
125
|
--format FORMAT Format of the report to produce (html, txt or md)
|
|
126
126
|
--report-file File to write the report to
|
|
127
127
|
--tool-debug Enable tool debug logging
|
|
128
|
-
--report-debug
|
|
129
|
-
|
|
128
|
+
--report-debug
|
|
129
|
+
--no-report-debug
|
|
130
|
+
Include debug messages in report (WARNING: can significantly increase report size)
|
|
130
131
|
If the tool is launched with an environment that can call `xdg-open`, the report
|
|
131
132
|
will be opened in a browser.
|
|
132
133
|
|
|
@@ -101,8 +101,9 @@ The following optional arguments are supported for this command:
|
|
|
101
101
|
--format FORMAT Format of the report to produce (html, txt or md)
|
|
102
102
|
--report-file File to write the report to
|
|
103
103
|
--tool-debug Enable tool debug logging
|
|
104
|
-
--report-debug
|
|
105
|
-
|
|
104
|
+
--report-debug
|
|
105
|
+
--no-report-debug
|
|
106
|
+
Include debug messages in report (WARNING: can significantly increase report size)
|
|
106
107
|
If the tool is launched with an environment that can call `xdg-open`, the report
|
|
107
108
|
will be opened in a browser.
|
|
108
109
|
|
|
@@ -14,6 +14,7 @@ import struct
|
|
|
14
14
|
import subprocess
|
|
15
15
|
import re
|
|
16
16
|
import sys
|
|
17
|
+
from ast import literal_eval
|
|
17
18
|
from datetime import date, timedelta
|
|
18
19
|
|
|
19
20
|
|
|
@@ -278,6 +279,19 @@ def get_property_pyudev(properties, key, fallback=""):
|
|
|
278
279
|
return ""
|
|
279
280
|
|
|
280
281
|
|
|
282
|
+
def find_ip_version(base_path, kind, hw_ver) -> bool:
|
|
283
|
+
"""Determine if an IP version is present on the system"""
|
|
284
|
+
b = os.path.join(base_path, "ip_discovery", "die", "0", kind, "0")
|
|
285
|
+
for key, expected_value in hw_ver.items():
|
|
286
|
+
p = os.path.join(b, key)
|
|
287
|
+
if not os.path.exists(p):
|
|
288
|
+
return False
|
|
289
|
+
v = int(read_file(p))
|
|
290
|
+
if v != expected_value:
|
|
291
|
+
return False
|
|
292
|
+
return True
|
|
293
|
+
|
|
294
|
+
|
|
281
295
|
def read_msr(msr, cpu):
|
|
282
296
|
"""Read a Model-Specific Register (MSR) value from the CPU."""
|
|
283
297
|
p = f"/dev/cpu/{cpu}/msr"
|
|
@@ -308,6 +322,16 @@ def running_ssh():
|
|
|
308
322
|
return "SSH_CLIENT" in os.environ or "SSH_TTY" in os.environ
|
|
309
323
|
|
|
310
324
|
|
|
325
|
+
def convert_string_to_bool(str_value) -> bool:
|
|
326
|
+
"""convert a string to a boolean value"""
|
|
327
|
+
try:
|
|
328
|
+
value = literal_eval(str_value)
|
|
329
|
+
except (SyntaxError, ValueError):
|
|
330
|
+
value = None
|
|
331
|
+
sys.exit(f"Invalid entry: {str_value}")
|
|
332
|
+
return bool(value)
|
|
333
|
+
|
|
334
|
+
|
|
311
335
|
def _git_describe() -> str:
|
|
312
336
|
"""Get the git description of the current commit"""
|
|
313
337
|
try:
|
|
@@ -142,7 +142,7 @@ class MissingDriver(S0i3Failure):
|
|
|
142
142
|
self.description = f"{slot} driver is missing"
|
|
143
143
|
self.explanation = (
|
|
144
144
|
f"No driver has been bound to PCI device {slot} "
|
|
145
|
-
"Without a driver, the hardware may be able to enter a low power
|
|
145
|
+
"Without a driver, the hardware may be able to enter a low power "
|
|
146
146
|
"state, but there may be spurious wake up events."
|
|
147
147
|
)
|
|
148
148
|
|
|
@@ -416,7 +416,7 @@ class LowHardwareSleepResidency(S0i3Failure):
|
|
|
416
416
|
super().__init__()
|
|
417
417
|
self.description = "System had low hardware sleep residency"
|
|
418
418
|
self.explanation = (
|
|
419
|
-
f"The system was asleep for {timedelta(seconds=duration)}, but only spent {percent:.2%} "
|
|
419
|
+
f"The system was asleep for {timedelta(seconds=duration)}, but only spent {percent/100:.2%} "
|
|
420
420
|
"of this time in a hardware sleep state. In sleep cycles that are at least "
|
|
421
421
|
"60 seconds long it's expected you spend above 90 percent of the cycle in "
|
|
422
422
|
"hardware sleep."
|
|
@@ -586,3 +586,15 @@ class RogAllyMcuPowerSave(S0i3Failure):
|
|
|
586
586
|
"The MCU powersave feature is disabled which will cause problems "
|
|
587
587
|
"with the controller after suspend/resume."
|
|
588
588
|
)
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
class DmcubTooOld(S0i3Failure):
|
|
592
|
+
"""DMCUB microcode is too old"""
|
|
593
|
+
|
|
594
|
+
def __init__(self, current, expected):
|
|
595
|
+
super().__init__()
|
|
596
|
+
self.description = "DMCUB microcode is too old"
|
|
597
|
+
self.explanation = (
|
|
598
|
+
f"The DMCUB microcode version {hex(current)} is older than the"
|
|
599
|
+
f"minimum suggested version {hex(expected)}."
|
|
600
|
+
)
|
|
@@ -26,6 +26,7 @@ class Headers: # pylint: disable=too-few-public-methods
|
|
|
26
26
|
|
|
27
27
|
MissingIasl = "ACPI extraction tool `iasl` is missing"
|
|
28
28
|
MissingEdidDecode = "EDID decoding tool `edid-decode` is missing"
|
|
29
|
+
MissingDiEdidDecode = "EDID decoding tool `di-edid-decode` is missing"
|
|
29
30
|
MissingEthtool = "Ethtool is missing"
|
|
30
31
|
InstallAction = "Attempting to install"
|
|
31
32
|
MissingFwupd = "Firmware update library `fwupd` is missing"
|
|
@@ -35,6 +36,7 @@ class Headers: # pylint: disable=too-few-public-methods
|
|
|
35
36
|
MissingTabulate = "Data library `tabulate` is missing"
|
|
36
37
|
MissingJinja2 = "Template library `jinja2` is missing"
|
|
37
38
|
MissingSeaborn = "Data visualization library `seaborn` is missing"
|
|
39
|
+
UnknownDistro = "No distro installation support available, install manually"
|
|
38
40
|
|
|
39
41
|
|
|
40
42
|
class DistroPackage:
|
|
@@ -71,7 +73,8 @@ class DistroPackage:
|
|
|
71
73
|
return False
|
|
72
74
|
installer = ["pacman", "-Sy", self.arch]
|
|
73
75
|
else:
|
|
74
|
-
|
|
76
|
+
print_color(Headers.UnknownDistro, "👀")
|
|
77
|
+
return True
|
|
75
78
|
|
|
76
79
|
try:
|
|
77
80
|
subprocess.check_call(installer)
|
|
@@ -188,6 +191,18 @@ class EdidDecodePackage(DistroPackage):
|
|
|
188
191
|
)
|
|
189
192
|
|
|
190
193
|
|
|
194
|
+
class DisplayInfoPackage(DistroPackage):
|
|
195
|
+
"""display info package"""
|
|
196
|
+
|
|
197
|
+
def __init__(self):
|
|
198
|
+
super().__init__(
|
|
199
|
+
deb="libdisplay-info-bin",
|
|
200
|
+
rpm="libdisplay-info",
|
|
201
|
+
arch="libdisplay-info",
|
|
202
|
+
message=Headers.MissingDiEdidDecode,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
|
|
191
206
|
class FwupdPackage(DistroPackage):
|
|
192
207
|
"""Fwupd package"""
|
|
193
208
|
|
|
@@ -261,7 +276,19 @@ class Installer(AmdTool):
|
|
|
261
276
|
package = EthtoolPackage()
|
|
262
277
|
if not package.install():
|
|
263
278
|
return False
|
|
279
|
+
# can be satisified by either edid-decode or di-edid-decode
|
|
264
280
|
if "edid-decode" in self.requirements:
|
|
281
|
+
try:
|
|
282
|
+
di_edid = (
|
|
283
|
+
subprocess.call(
|
|
284
|
+
["di-edid-decode", "--help"],
|
|
285
|
+
stdout=subprocess.DEVNULL,
|
|
286
|
+
stderr=subprocess.DEVNULL,
|
|
287
|
+
)
|
|
288
|
+
== 255
|
|
289
|
+
)
|
|
290
|
+
except FileNotFoundError:
|
|
291
|
+
di_edid = False
|
|
265
292
|
try:
|
|
266
293
|
edid = (
|
|
267
294
|
subprocess.call(
|
|
@@ -271,7 +298,12 @@ class Installer(AmdTool):
|
|
|
271
298
|
)
|
|
272
299
|
except FileNotFoundError:
|
|
273
300
|
edid = False
|
|
274
|
-
if not edid:
|
|
301
|
+
if not di_edid and not edid:
|
|
302
|
+
# try to install di-edid-decode first
|
|
303
|
+
package = DisplayInfoPackage()
|
|
304
|
+
if package.install():
|
|
305
|
+
return True
|
|
306
|
+
# fall back to edid-decode instead
|
|
275
307
|
package = EdidDecodePackage()
|
|
276
308
|
if not package.install():
|
|
277
309
|
return False
|
|
@@ -19,6 +19,7 @@ def get_kernel_command_line() -> str:
|
|
|
19
19
|
"apparmor",
|
|
20
20
|
"audit",
|
|
21
21
|
"auto",
|
|
22
|
+
"bluetooth.disable_ertm",
|
|
22
23
|
"boot",
|
|
23
24
|
"BOOT_IMAGE",
|
|
24
25
|
"console",
|
|
@@ -29,6 +30,7 @@ def get_kernel_command_line() -> str:
|
|
|
29
30
|
"earlycon",
|
|
30
31
|
"earlyprintk",
|
|
31
32
|
"ether",
|
|
33
|
+
"init",
|
|
32
34
|
"initrd",
|
|
33
35
|
"ip",
|
|
34
36
|
"LANG",
|
|
@@ -47,7 +49,9 @@ def get_kernel_command_line() -> str:
|
|
|
47
49
|
"nfs.nfs4_unique_id",
|
|
48
50
|
"nfsroot",
|
|
49
51
|
"noplymouth",
|
|
52
|
+
"nowatchdog",
|
|
50
53
|
"ostree",
|
|
54
|
+
"preempt",
|
|
51
55
|
"quiet",
|
|
52
56
|
"rd.dm.uuid",
|
|
53
57
|
"rd.luks.allow-discards",
|
|
@@ -66,12 +70,15 @@ def get_kernel_command_line() -> str:
|
|
|
66
70
|
"ro",
|
|
67
71
|
"root",
|
|
68
72
|
"rootflags",
|
|
73
|
+
"rootfstype",
|
|
69
74
|
"roothash",
|
|
70
75
|
"rw",
|
|
71
76
|
"security",
|
|
77
|
+
"selinux",
|
|
72
78
|
"showopts",
|
|
73
79
|
"splash",
|
|
74
80
|
"swap",
|
|
81
|
+
"systemd.machine_id",
|
|
75
82
|
"systemd.mask",
|
|
76
83
|
"systemd.show_status",
|
|
77
84
|
"systemd.unit",
|
|
@@ -82,6 +89,7 @@ def get_kernel_command_line() -> str:
|
|
|
82
89
|
"verbose",
|
|
83
90
|
"vt.handoff",
|
|
84
91
|
"zfs",
|
|
92
|
+
"zswap.enabled",
|
|
85
93
|
]
|
|
86
94
|
# remove anything that starts with something in filtered from cmdline
|
|
87
95
|
return " ".join([x for x in cmdline.split() if not x.startswith(tuple(filtered))])
|
|
@@ -26,6 +26,7 @@ from amd_debug.common import (
|
|
|
26
26
|
apply_prefix_wrapper,
|
|
27
27
|
BIT,
|
|
28
28
|
clear_temporary_message,
|
|
29
|
+
find_ip_version,
|
|
29
30
|
get_distro,
|
|
30
31
|
get_pretty_distro,
|
|
31
32
|
get_property_pyudev,
|
|
@@ -48,6 +49,7 @@ from amd_debug.failures import (
|
|
|
48
49
|
DevSlpDiskIssue,
|
|
49
50
|
DevSlpHostIssue,
|
|
50
51
|
DMArNotEnabled,
|
|
52
|
+
DmcubTooOld,
|
|
51
53
|
DmiNotSetup,
|
|
52
54
|
FadtWrong,
|
|
53
55
|
I2CHidBug,
|
|
@@ -132,22 +134,23 @@ class PrerequisiteValidator(AmdTool):
|
|
|
132
134
|
if len(edids) == 0:
|
|
133
135
|
self.db.record_debug("No EDID data found")
|
|
134
136
|
return True
|
|
135
|
-
|
|
136
137
|
for name, p in edids.items():
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
138
|
+
output = None
|
|
139
|
+
for tool in ["di-edid-decode", "edid-decode"]:
|
|
140
|
+
try:
|
|
141
|
+
cmd = [tool, p]
|
|
142
|
+
output = subprocess.check_output(
|
|
143
|
+
cmd, stderr=subprocess.DEVNULL
|
|
144
|
+
).decode("utf-8", errors="ignore")
|
|
145
|
+
break
|
|
146
|
+
except FileNotFoundError:
|
|
147
|
+
self.db.record_debug(f"{cmd} not installed")
|
|
148
|
+
except subprocess.CalledProcessError as e:
|
|
149
|
+
pass
|
|
150
|
+
if not output:
|
|
151
|
+
self.db.record_prereq("Failed to capture EDID table", "👀")
|
|
152
|
+
else:
|
|
153
|
+
self.db.record_debug(apply_prefix_wrapper(f"EDID for {name}:", output))
|
|
151
154
|
return True
|
|
152
155
|
|
|
153
156
|
def check_amdgpu(self):
|
|
@@ -239,7 +242,6 @@ class PrerequisiteValidator(AmdTool):
|
|
|
239
242
|
for dev in self.pyudev.list_devices(subsystem="pci", DRIVER="nvme"):
|
|
240
243
|
# https://git.kernel.org/torvalds/c/e79a10652bbd3
|
|
241
244
|
if minimum_kernel(6, 10):
|
|
242
|
-
self.db.record_debug("New enough kernel to avoid NVME check")
|
|
243
245
|
break
|
|
244
246
|
pci_slot_name = dev.properties["PCI_SLOT_NAME"]
|
|
245
247
|
vendor = dev.properties.get("ID_VENDOR_FROM_DATABASE", "")
|
|
@@ -313,7 +315,6 @@ class PrerequisiteValidator(AmdTool):
|
|
|
313
315
|
# not needed to check in newer kernels
|
|
314
316
|
# see https://github.com/torvalds/linux/commit/77f1972bdcf7513293e8bbe376b9fe837310ee9c
|
|
315
317
|
if minimum_kernel(6, 10):
|
|
316
|
-
self.db.record_debug("New enough kernel to avoid HSMP check")
|
|
317
318
|
return True
|
|
318
319
|
f = os.path.join("/", "boot", f"config-{platform.uname().release}")
|
|
319
320
|
if os.path.exists(f):
|
|
@@ -395,6 +396,53 @@ class PrerequisiteValidator(AmdTool):
|
|
|
395
396
|
)
|
|
396
397
|
return True
|
|
397
398
|
|
|
399
|
+
def check_dpia_pg_dmcub(self):
|
|
400
|
+
"""Check if DMUB is new enough to PG DPIA when no USB4 present"""
|
|
401
|
+
usb4_found = False
|
|
402
|
+
for device in self.pyudev.list_devices(subsystem="pci", PCI_CLASS="C0340"):
|
|
403
|
+
usb4_found = True
|
|
404
|
+
break
|
|
405
|
+
if usb4_found:
|
|
406
|
+
self.db.record_debug("USB4 routers found, no need to check DMCUB version")
|
|
407
|
+
return True
|
|
408
|
+
# Check if matching DCN present
|
|
409
|
+
for device in self.pyudev.list_devices(subsystem="pci"):
|
|
410
|
+
current = None
|
|
411
|
+
klass = device.properties.get("PCI_CLASS")
|
|
412
|
+
if klass not in ["30000", "38000"]:
|
|
413
|
+
continue
|
|
414
|
+
pci_id = device.properties.get("PCI_ID")
|
|
415
|
+
if not pci_id.startswith("1002"):
|
|
416
|
+
continue
|
|
417
|
+
hw_ver = {"major": 3, "minor": 5, "revision": 0}
|
|
418
|
+
if not find_ip_version(device.sys_path, "DMU", hw_ver):
|
|
419
|
+
continue
|
|
420
|
+
|
|
421
|
+
# DCN was found, lookup version from sysfs
|
|
422
|
+
p = os.path.join(device.sys_path, "fw_version", "dmcub_fw_version")
|
|
423
|
+
if os.path.exists(p):
|
|
424
|
+
current = int(read_file(p), 16)
|
|
425
|
+
|
|
426
|
+
# no sysfs, try to look for version from debugfs
|
|
427
|
+
if not current:
|
|
428
|
+
slot = device.properties["PCI_SLOT_NAME"]
|
|
429
|
+
p = os.path.join(
|
|
430
|
+
"/", "sys", "kernel", "debug", "dri", slot, "amdgpu_firmware_info"
|
|
431
|
+
)
|
|
432
|
+
contents = read_file(p)
|
|
433
|
+
for line in contents.split("\n"):
|
|
434
|
+
if not line.startswith("DMCUB"):
|
|
435
|
+
continue
|
|
436
|
+
current = int(line.split()[-1], 16)
|
|
437
|
+
if current:
|
|
438
|
+
expected = 0x09001B00
|
|
439
|
+
if current >= expected:
|
|
440
|
+
return True
|
|
441
|
+
self.db.record_prereq("DMCUB Firmware is outdated", "❌")
|
|
442
|
+
self.failures += [DmcubTooOld(current, expected)]
|
|
443
|
+
return False
|
|
444
|
+
return True
|
|
445
|
+
|
|
398
446
|
def check_usb4(self):
|
|
399
447
|
"""Check if the thunderbolt driver is loaded"""
|
|
400
448
|
slots = []
|
|
@@ -474,7 +522,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
474
522
|
f"{keys['sys_vendor']} {keys['product_name']} ({keys['product_family']})",
|
|
475
523
|
"💻",
|
|
476
524
|
)
|
|
477
|
-
debug_str = "DMI
|
|
525
|
+
debug_str = "DMI|value\n"
|
|
478
526
|
for key, value in keys.items():
|
|
479
527
|
if (
|
|
480
528
|
"product_name" in key
|
|
@@ -482,7 +530,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
482
530
|
or "product_family" in key
|
|
483
531
|
):
|
|
484
532
|
continue
|
|
485
|
-
debug_str += f"{key}
|
|
533
|
+
debug_str += f"{key}| {value}\n"
|
|
486
534
|
self.db.record_debug(debug_str)
|
|
487
535
|
return True
|
|
488
536
|
|
|
@@ -698,9 +746,13 @@ class PrerequisiteValidator(AmdTool):
|
|
|
698
746
|
interface = device.properties.get("INTERFACE")
|
|
699
747
|
cmd = ["ethtool", interface]
|
|
700
748
|
wol_supported = False
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
749
|
+
try:
|
|
750
|
+
output = subprocess.check_output(cmd, stderr=subprocess.DEVNULL).decode(
|
|
751
|
+
"utf-8"
|
|
752
|
+
)
|
|
753
|
+
except FileNotFoundError:
|
|
754
|
+
self.db.record_prereq(f"ethtool is missing", "👀")
|
|
755
|
+
return True
|
|
704
756
|
for line in output.split("\n"):
|
|
705
757
|
if "Supports Wake-on" in line:
|
|
706
758
|
val = line.split(":")[1].strip()
|
|
@@ -859,7 +911,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
859
911
|
devices = []
|
|
860
912
|
for dev in self.pyudev.list_devices(subsystem="pci"):
|
|
861
913
|
devices.append(dev)
|
|
862
|
-
debug_str = "PCI
|
|
914
|
+
debug_str = "PCI Slot | Vendor | Class | ID | ACPI path\n"
|
|
863
915
|
for dev in devices:
|
|
864
916
|
pci_id = dev.properties["PCI_ID"].lower()
|
|
865
917
|
pci_slot_name = dev.properties["PCI_SLOT_NAME"]
|
|
@@ -882,15 +934,12 @@ class PrerequisiteValidator(AmdTool):
|
|
|
882
934
|
p = os.path.join(dev.sys_path, "firmware_node", "path")
|
|
883
935
|
if os.path.exists(p):
|
|
884
936
|
acpi = read_file(p)
|
|
885
|
-
debug_str += (
|
|
886
|
-
f"{prefix}{pci_slot_name} : "
|
|
887
|
-
f"{database_vendor} {database_class} [{pci_id}] : {acpi}\n"
|
|
888
|
-
)
|
|
889
937
|
else:
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
938
|
+
acpi = ""
|
|
939
|
+
debug_str += (
|
|
940
|
+
f"{prefix}{pci_slot_name} | "
|
|
941
|
+
f"{database_vendor} | {database_class} | {pci_id} | {acpi}\n"
|
|
942
|
+
)
|
|
894
943
|
if debug_str:
|
|
895
944
|
self.db.record_debug(debug_str)
|
|
896
945
|
|
|
@@ -907,12 +956,8 @@ class PrerequisiteValidator(AmdTool):
|
|
|
907
956
|
if status == 0:
|
|
908
957
|
continue
|
|
909
958
|
devices.append(dev)
|
|
910
|
-
debug_str = "ACPI name
|
|
959
|
+
debug_str = "ACPI name | ACPI path | Kernel driver\n"
|
|
911
960
|
for dev in devices:
|
|
912
|
-
if dev == devices[-1]:
|
|
913
|
-
prefix = "└─"
|
|
914
|
-
else:
|
|
915
|
-
prefix = "│ "
|
|
916
961
|
p = os.path.join(dev.sys_path, "path")
|
|
917
962
|
pth = read_file(p)
|
|
918
963
|
p = os.path.join(dev.sys_path, "physical_node", "driver")
|
|
@@ -920,7 +965,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
920
965
|
driver = os.path.basename(os.readlink(p))
|
|
921
966
|
else:
|
|
922
967
|
driver = None
|
|
923
|
-
debug_str += f"{
|
|
968
|
+
debug_str += f"{dev.sys_name} | {pth} | {driver}\n"
|
|
924
969
|
if debug_str:
|
|
925
970
|
self.db.record_debug(debug_str)
|
|
926
971
|
return True
|
|
@@ -949,6 +994,8 @@ class PrerequisiteValidator(AmdTool):
|
|
|
949
994
|
stderr=subprocess.DEVNULL,
|
|
950
995
|
)
|
|
951
996
|
self.db.record_debug_file(f"{prefix}.dsl")
|
|
997
|
+
except FileNotFoundError as e:
|
|
998
|
+
self.db.record_prereq(f"Failed to capture ACPI table: {e}", "👀")
|
|
952
999
|
except subprocess.CalledProcessError as e:
|
|
953
1000
|
self.db.record_prereq(
|
|
954
1001
|
f"Failed to capture ACPI table: {e.output}", "👀"
|
|
@@ -1098,14 +1145,16 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1098
1145
|
if not found_iommu:
|
|
1099
1146
|
self.db.record_prereq("IOMMU disabled", "✅")
|
|
1100
1147
|
return True
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
):
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1148
|
+
p = os.path.join("/", "sys", "firmware", "acpi", "tables", "IVRS")
|
|
1149
|
+
with open(p, "rb") as f:
|
|
1150
|
+
data = f.read()
|
|
1151
|
+
if len(data) < 40:
|
|
1152
|
+
raise ValueError(
|
|
1153
|
+
"IVRS table appears too small to contain virtualization info."
|
|
1154
|
+
)
|
|
1155
|
+
virt_info = struct.unpack_from("I", data, 36)[0]
|
|
1156
|
+
debug_str += f"Virtualization info: 0x{virt_info:x}"
|
|
1157
|
+
found_dmar = (virt_info & 0x2) != 0
|
|
1109
1158
|
self.db.record_debug(debug_str)
|
|
1110
1159
|
if not found_dmar:
|
|
1111
1160
|
self.db.record_prereq(
|
|
@@ -1205,6 +1254,7 @@ class PrerequisiteValidator(AmdTool):
|
|
|
1205
1254
|
self.check_smt,
|
|
1206
1255
|
self.check_iommu,
|
|
1207
1256
|
self.check_asus_rog_ally,
|
|
1257
|
+
self.check_dpia_pg_dmcub,
|
|
1208
1258
|
]
|
|
1209
1259
|
|
|
1210
1260
|
checks += [
|
|
@@ -9,6 +9,7 @@ import sqlite3
|
|
|
9
9
|
|
|
10
10
|
from datetime import date, timedelta, datetime
|
|
11
11
|
from amd_debug.common import (
|
|
12
|
+
convert_string_to_bool,
|
|
12
13
|
colorize_choices,
|
|
13
14
|
is_root,
|
|
14
15
|
relaunch_sudo,
|
|
@@ -32,6 +33,7 @@ class Defaults:
|
|
|
32
33
|
since = date.today() - timedelta(days=60)
|
|
33
34
|
until = date.today() + timedelta(days=1)
|
|
34
35
|
format_choices = ["txt", "md", "html", "stdout"]
|
|
36
|
+
boolean_choices = ["true", "false"]
|
|
35
37
|
|
|
36
38
|
|
|
37
39
|
class Headers:
|
|
@@ -47,6 +49,7 @@ class Headers:
|
|
|
47
49
|
FormatDescription = "What format to output the report in"
|
|
48
50
|
MaxDurationDescription = "What is the maximum suspend cycle length (seconds)"
|
|
49
51
|
MaxWaitDescription = "What is the maximum time between suspend cycles (seconds)"
|
|
52
|
+
ReportDebugDescription = "Enable debug output in report (increased size)"
|
|
50
53
|
|
|
51
54
|
|
|
52
55
|
def display_report_file(fname, fmt) -> None:
|
|
@@ -85,7 +88,7 @@ def get_report_format() -> str:
|
|
|
85
88
|
return "html"
|
|
86
89
|
|
|
87
90
|
|
|
88
|
-
def prompt_report_arguments(since, until, fname, fmt) -> str:
|
|
91
|
+
def prompt_report_arguments(since, until, fname, fmt, report_debug) -> str:
|
|
89
92
|
"""Prompt user for report configuration"""
|
|
90
93
|
if not since:
|
|
91
94
|
default = Defaults.since
|
|
@@ -114,7 +117,16 @@ def prompt_report_arguments(since, until, fname, fmt) -> str:
|
|
|
114
117
|
fmt = get_report_format()
|
|
115
118
|
if fmt not in Defaults.format_choices:
|
|
116
119
|
sys.exit(f"Invalid format: {fmt}")
|
|
117
|
-
|
|
120
|
+
if report_debug is None:
|
|
121
|
+
inp = (
|
|
122
|
+
input(
|
|
123
|
+
f"{Headers.ReportDebugDescription} ({colorize_choices(Defaults.boolean_choices, "true")})? "
|
|
124
|
+
)
|
|
125
|
+
.lower()
|
|
126
|
+
.capitalize()
|
|
127
|
+
)
|
|
128
|
+
report_debug = True if not inp else convert_string_to_bool(inp)
|
|
129
|
+
return [since, until, get_report_file(fname, fmt), fmt, report_debug]
|
|
118
130
|
|
|
119
131
|
|
|
120
132
|
def prompt_test_arguments(duration, wait, count, rand) -> list:
|
|
@@ -157,7 +169,9 @@ def prompt_test_arguments(duration, wait, count, rand) -> list:
|
|
|
157
169
|
def report(since, until, fname, fmt, tool_debug, report_debug) -> bool:
|
|
158
170
|
"""Generate a report from previous sleep cycles"""
|
|
159
171
|
try:
|
|
160
|
-
since, until, fname, fmt = prompt_report_arguments(
|
|
172
|
+
since, until, fname, fmt, report_debug = prompt_report_arguments(
|
|
173
|
+
since, until, fname, fmt, report_debug
|
|
174
|
+
)
|
|
161
175
|
except KeyboardInterrupt:
|
|
162
176
|
sys.exit("\nReport generation cancelled")
|
|
163
177
|
try:
|
|
@@ -197,6 +211,14 @@ def run_test_cycle(
|
|
|
197
211
|
print("Failed to install dependencies")
|
|
198
212
|
return False
|
|
199
213
|
|
|
214
|
+
try:
|
|
215
|
+
duration, wait, count = prompt_test_arguments(duration, wait, count, rand)
|
|
216
|
+
since, until, fname, fmt, report_debug = prompt_report_arguments(
|
|
217
|
+
datetime.now().isoformat(), Defaults.until.isoformat(), fname, fmt, True
|
|
218
|
+
)
|
|
219
|
+
except KeyboardInterrupt:
|
|
220
|
+
sys.exit("\nTest cancelled")
|
|
221
|
+
|
|
200
222
|
try:
|
|
201
223
|
app = PrerequisiteValidator(debug)
|
|
202
224
|
run = app.run()
|
|
@@ -207,37 +229,32 @@ def run_test_cycle(
|
|
|
207
229
|
|
|
208
230
|
if run or force:
|
|
209
231
|
app = SleepValidator(tool_debug=debug, bios_debug=bios_debug)
|
|
210
|
-
try:
|
|
211
|
-
duration, wait, count = prompt_test_arguments(duration, wait, count, rand)
|
|
212
|
-
since, until, fname, fmt = prompt_report_arguments(
|
|
213
|
-
datetime.now().isoformat(), Defaults.until.isoformat(), fname, fmt
|
|
214
|
-
)
|
|
215
|
-
except KeyboardInterrupt:
|
|
216
|
-
sys.exit("\nTest cancelled")
|
|
217
232
|
|
|
218
|
-
app.run(
|
|
233
|
+
run = app.run(
|
|
219
234
|
duration=duration,
|
|
220
235
|
wait=wait,
|
|
221
236
|
count=count,
|
|
222
237
|
rand=rand,
|
|
223
238
|
logind=logind,
|
|
224
239
|
)
|
|
240
|
+
else:
|
|
241
|
+
since = None
|
|
242
|
+
until = None
|
|
243
|
+
|
|
244
|
+
app = SleepReport(
|
|
245
|
+
since=since,
|
|
246
|
+
until=until,
|
|
247
|
+
fname=fname,
|
|
248
|
+
fmt=fmt,
|
|
249
|
+
tool_debug=debug,
|
|
250
|
+
report_debug=report_debug,
|
|
251
|
+
)
|
|
252
|
+
app.run()
|
|
225
253
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
until=until,
|
|
229
|
-
fname=fname,
|
|
230
|
-
fmt=fmt,
|
|
231
|
-
tool_debug=debug,
|
|
232
|
-
report_debug=True,
|
|
233
|
-
)
|
|
234
|
-
app.run()
|
|
235
|
-
|
|
236
|
-
# open report in browser if it's html
|
|
237
|
-
display_report_file(fname, fmt)
|
|
254
|
+
# open report in browser if it's html
|
|
255
|
+
display_report_file(fname, fmt)
|
|
238
256
|
|
|
239
|
-
|
|
240
|
-
return False
|
|
257
|
+
return True
|
|
241
258
|
|
|
242
259
|
|
|
243
260
|
def install(debug) -> None:
|
|
@@ -294,10 +311,8 @@ def parse_args():
|
|
|
294
311
|
test_cmd.add_argument(
|
|
295
312
|
"--random",
|
|
296
313
|
action="store_true",
|
|
297
|
-
help=
|
|
298
|
-
|
|
299
|
-
"--duration and --wait arguments as an upper bound",
|
|
300
|
-
),
|
|
314
|
+
help="Run sleep cycles for random durations and wait, using the "
|
|
315
|
+
"--duration and --wait arguments as an upper bound",
|
|
301
316
|
)
|
|
302
317
|
test_cmd.add_argument(
|
|
303
318
|
"--force",
|
|
@@ -347,7 +362,7 @@ def parse_args():
|
|
|
347
362
|
)
|
|
348
363
|
report_cmd.add_argument(
|
|
349
364
|
"--report-debug",
|
|
350
|
-
action=
|
|
365
|
+
action=argparse.BooleanOptionalAction,
|
|
351
366
|
help="Include debug messages in report (WARNING: can significantly increase report size)",
|
|
352
367
|
)
|
|
353
368
|
|