1+ from __future__ import annotations
2+
3+ import os , subprocess
4+
5+ from Assets .core import config as cfg
6+ from Assets .core import platform as plat
7+ from zenmaster .smu import secure_boot_enabled
8+
9+
10+ PROC_CPUINFO = "/proc/cpuinfo"
11+ SYS_CPUFREQ_MAX = "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"
12+ SYS_DMI_ID_DIR = "/sys/class/dmi/id"
13+
14+
15+ def _cpuinfo_dict () -> dict [str , str ]:
16+ result : dict [str , str ] = {}
17+ try :
18+ with open (PROC_CPUINFO ) as f :
19+ for line in f :
20+ if ":" not in line :
21+ if line .strip () == "" and result :
22+ break
23+ continue
24+ key , _ , value = line .partition (":" )
25+ result [key .strip ()] = value .strip ()
26+ except OSError :
27+ pass
28+ return result
29+
30+
31+ def max_clock_mhz () -> int :
32+ if plat .IS_MACOS :
33+ hz = _sysctl_int ("hw.cpufrequency_max" )
34+ if hz <= 0 :
35+ return 0
36+ mhz = hz // 1_000_000
37+ return ((mhz + 499 ) // 500 ) * 500
38+ try :
39+ with open (SYS_CPUFREQ_MAX ) as f :
40+ khz = int (f .read ().strip ())
41+ return ((khz // 1000 + 499 ) // 500 ) * 500
42+ except (OSError , ValueError ):
43+ pass
44+ try :
45+ mhz = float (_cpuinfo_dict ().get ("cpu MHz" , "0" ))
46+ except ValueError :
47+ mhz = 0
48+ if mhz <= 0 :
49+ return 0
50+ return ((int (mhz ) + 499 ) // 500 ) * 500
51+
52+
53+ _SMU_INSTALL_GUIDE = f"Install guide: { cfg .RYZEN_SMU_WIKI_URL } "
54+
55+
56+ def check_ryzen_smu () -> str | None :
57+ if not secure_boot_enabled ():
58+ return None
59+
60+ from zenmaster .smu import module_status
61+
62+ st = module_status ()
63+ if st .ok :
64+ return None
65+
66+ if st .reason == "unknown" :
67+ message = (
68+ f"ryzen_smu is loaded but its version cannot be determined "
69+ f"(minimum required: { st .min_version } )"
70+ )
71+ elif st .reason == "too_old" :
72+ message = f"ryzen_smu version { st .version } is too old (minimum required: { st .min_version } )"
73+ elif st .reason == "not_installed" :
74+ message = "ryzen_smu kernel module is not installed"
75+ elif st .reason == "unsigned" :
76+ message = "ryzen_smu is installed but not signed for Secure Boot"
77+ else :
78+ message = "ryzen_smu is installed but not loaded"
79+
80+ return f"{ message } .\n \n { _SMU_INSTALL_GUIDE } "
81+
82+
83+ def _read_sysfs (path : str ) -> str | None :
84+ try :
85+ with open (path ) as f :
86+ return f .read ().strip ()
87+ except OSError :
88+ return None
89+
90+
91+ def _parse_device_info () -> dict [str , str ]:
92+ return {
93+ "name" : _read_sysfs (os .path .join (SYS_DMI_ID_DIR , "product_name" )) or "N/A" ,
94+ "producer" : _read_sysfs (os .path .join (SYS_DMI_ID_DIR , "sys_vendor" )) or "N/A" ,
95+ "model" : _read_sysfs (os .path .join (SYS_DMI_ID_DIR , "board_name" )) or "N/A" ,
96+ }
97+
98+
99+ def _lspci_vga () -> str :
100+ try :
101+ result = subprocess .run (
102+ ["lspci" ],
103+ capture_output = True , text = True , timeout = 5 ,
104+ )
105+ lines = [l for l in result .stdout .splitlines () if "VGA" in l or "Display" in l ]
106+ return "\n " .join (lines ).lower ()
107+ except Exception :
108+ return ""
109+
110+
111+ def _has_discrete_rx7700s () -> bool :
112+ vga = _lspci_vga ()
113+ return "7700s" in vga or "rx 7700s" in vga
114+
115+
116+ def _sysctl_int (name : str ) -> int :
117+ try :
118+ out = subprocess .run (["sysctl" , "-n" , name ], capture_output = True , text = True , timeout = 5 )
119+ except (OSError , subprocess .SubprocessError ):
120+ return 0
121+ try :
122+ return int (out .stdout .strip ())
123+ except ValueError :
124+ return 0
125+
126+
127+ def _detect_framework_variant () -> str :
128+ product = (_read_sysfs (os .path .join (SYS_DMI_ID_DIR , "product_name" )) or "" ).lower ()
129+ mfr = (_read_sysfs (os .path .join (SYS_DMI_ID_DIR , "sys_vendor" )) or "" ).lower ()
130+
131+ if "framework" not in mfr :
132+ return ""
133+
134+ if "laptop 16" in product and "7040" in product :
135+ if _has_discrete_rx7700s ():
136+ return "AMDFrameworkLaptop16Ryzen7040_RX7700S"
137+ return "AMDFrameworkLaptop16Ryzen7040"
138+
139+ if "laptop 13" in product and ("7040" in product or "ai 300" in product or "ryzen ai 300" in product ):
140+ return "AMDFrameworkLaptop13Ryzen7040_RyzenAI300"
141+
142+ return ""
143+
144+
145+ _detected_family_cache : str | None = None
146+
147+
148+ def detected_family () -> str :
149+ global _detected_family_cache
150+ if _detected_family_cache is None :
151+ from zenmaster .hardware import detect as zm_detect
152+ _detected_family_cache = zm_detect ().family
153+ return _detected_family_cache
154+
155+
156+ def detect () -> None :
157+ from zenmaster .hardware import detect as zm_detect
158+ info = zm_detect ()
159+ cfg .set_config ("Info" , "CPU" , info .name or "Unknown" )
160+ cfg .set_config ("Info" , "Signature" ,
161+ f"Family { info .cpu_family_int } , Model { info .cpu_model_int } , Stepping { info .cpu_stepping_int } " )
162+ _compute_codename (info )
163+
164+ variant = _detect_framework_variant ()
165+ cfg .set_config ("Info" , "Variant" , variant )
166+
167+ cfg .save ()
168+
169+
170+ def _compute_codename (info = None ) -> None :
171+ if info is None :
172+ raw_cpu = cfg .get ("Info" , "CPU" )
173+ signature = cfg .get ("Info" , "Signature" )
174+ try :
175+ words = signature .split ()
176+ cpu_family = int (words [words .index ("Family" ) + 1 ].rstrip ("," ))
177+ cpu_model = int (words [words .index ("Model" ) + 1 ].rstrip ("," ))
178+ except (ValueError , IndexError ):
179+ cfg .set_config ("Info" , "Architecture" , "Unknown" )
180+ cfg .set_config ("Info" , "Family" , "Unknown" )
181+ cfg .set_config ("Info" , "Type" , "Unknown" )
182+ return
183+ from zenmaster .hardware import resolve
184+ info = resolve (raw_cpu , cpu_family , cpu_model )
185+
186+ from zenmaster .runner import is_supported
187+ cpu_type = info .type
188+ if cpu_type in ("Amd_Apu" , "Amd_Desktop_Cpu" ) and not is_supported (info .family ):
189+ cpu_type = "Unknown"
190+ cfg .set_config ("Info" , "Architecture" , info .arch )
191+ cfg .set_config ("Info" , "Family" , info .family )
192+ cfg .set_config ("Info" , "Type" , cpu_type )
0 commit comments