1 // Copyright (C) 2003 Dolphin Project.
2
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0.
6
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
11
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14
15 // Official SVN repository and contact information can be found at
16 // http://code.google.com/p/dolphin-emu/
17
18 #include "ppsspp_config.h"
19
20 #include <sstream>
21
22 #if PPSSPP_PLATFORM(IOS) || PPSSPP_PLATFORM(MAC)
23 #include <sys/sysctl.h>
24 #endif
25
26
27 #if PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64)
28
29 #include <ctype.h>
30
31 #include "Common/Common.h"
32 #include "Common/CPUDetect.h"
33 #include "Common/StringUtils.h"
34 #include "Common/File/FileUtil.h"
35 #include "Common/Data/Encoding/Utf8.h"
36
37 #if PPSSPP_PLATFORM(WINDOWS)
38 #if PPSSPP_PLATFORM(UWP)
39 // TODO: Maybe we can move the implementation here?
40 std::string GetCPUBrandString();
41 #else
42 // No CPUID on ARM, so we'll have to read the registry
43 #include <windows.h>
GetCPUBrandString()44 std::string GetCPUBrandString() {
45 std::string cpu_string;
46
47 HKEY key;
48 LSTATUS result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_READ, &key);
49 if (result == ERROR_SUCCESS) {
50 DWORD size = 0;
51 DWORD type = REG_SZ;
52 RegQueryValueEx(key, L"ProcessorNameString", NULL, &type, NULL, &size);
53 LPBYTE buff = (LPBYTE)malloc(size);
54 if (buff != NULL) {
55 RegQueryValueEx(key, L"ProcessorNameString", NULL, &type, buff, &size);
56 cpu_string = ConvertWStringToUTF8((wchar_t*)buff);
57 free(buff);
58 }
59 RegCloseKey(key);
60 }
61
62 if (cpu_string.empty())
63 return "Unknown";
64 else
65 return cpu_string;
66 }
67 #endif
68 #endif
69
70 // Only Linux platforms have /proc/cpuinfo
71 #if PPSSPP_PLATFORM(LINUX)
72 const char procfile[] = "/proc/cpuinfo";
73 // https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-devices-system-cpu
74 const char syscpupresentfile[] = "/sys/devices/system/cpu/present";
75
GetCPUString()76 std::string GetCPUString() {
77 std::string procdata;
78 bool readSuccess = File::ReadFileToString(true, Path(procfile), procdata);
79 std::istringstream file(procdata);
80 std::string cpu_string;
81
82 if (readSuccess) {
83 std::string line, marker = "Hardware\t: ";
84 while (std::getline(file, line)) {
85 if (line.find(marker) != std::string::npos) {
86 cpu_string = line.substr(marker.length());
87 }
88 }
89 }
90
91 if (cpu_string.empty())
92 cpu_string = "Unknown";
93 else if (cpu_string.back() == '\n')
94 cpu_string.pop_back(); // Drop the new-line character
95
96 return cpu_string;
97 }
98
GetCPUBrandString()99 std::string GetCPUBrandString() {
100 std::string procdata;
101 bool readSuccess = File::ReadFileToString(true, Path(procfile), procdata);
102 std::istringstream file(procdata);
103 std::string brand_string;
104
105 if (readSuccess) {
106 std::string line, marker = "Processor\t: ";
107 while (std::getline(file, line)) {
108 if (line.find(marker) != std::string::npos) {
109 brand_string = line.substr(marker.length());
110 if (brand_string.length() != 0 && !isdigit(brand_string[0])) {
111 break;
112 }
113 }
114 }
115 }
116
117 if (brand_string.empty())
118 brand_string = "Unknown";
119 else if (brand_string.back() == '\n')
120 brand_string.pop_back(); // Drop the new-line character
121
122 return brand_string;
123 }
124
GetCPUImplementer()125 unsigned char GetCPUImplementer()
126 {
127 std::string line, marker = "CPU implementer\t: ";
128 unsigned char implementer = 0;
129
130 std::string procdata;
131 if (!File::ReadFileToString(true, Path(procfile), procdata))
132 return 0;
133 std::istringstream file(procdata);
134
135 while (std::getline(file, line))
136 {
137 if (line.find(marker) != std::string::npos)
138 {
139 line = line.substr(marker.length());
140 sscanf(line.c_str(), "0x%02hhx", &implementer);
141 break;
142 }
143 }
144
145 return implementer;
146 }
147
GetCPUPart()148 unsigned short GetCPUPart()
149 {
150 std::string line, marker = "CPU part\t: ";
151 unsigned short part = 0;
152
153 std::string procdata;
154 if (!File::ReadFileToString(true, Path(procfile), procdata))
155 return 0;
156 std::istringstream file(procdata);
157
158 while (std::getline(file, line))
159 {
160 if (line.find(marker) != std::string::npos)
161 {
162 line = line.substr(marker.length());
163 sscanf(line.c_str(), "0x%03hx", &part);
164 break;
165 }
166 }
167
168 return part;
169 }
170
CheckCPUFeature(const std::string & feature)171 bool CheckCPUFeature(const std::string& feature)
172 {
173 std::string line, marker = "Features\t: ";
174
175 std::string procdata;
176 if (!File::ReadFileToString(true, Path(procfile), procdata))
177 return false;
178 std::istringstream file(procdata);
179 while (std::getline(file, line))
180 {
181 if (line.find(marker) != std::string::npos)
182 {
183 std::stringstream line_stream(line);
184 std::string token;
185 while (std::getline(line_stream, token, ' '))
186 {
187 if (token == feature)
188 return true;
189 }
190 }
191 }
192
193 return false;
194 }
195
GetCoreCount()196 int GetCoreCount()
197 {
198 std::string line, marker = "processor\t: ";
199 int cores = 1;
200
201 std::string presentData;
202 bool presentSuccess = File::ReadFileToString(true, Path(syscpupresentfile), presentData);
203 std::istringstream presentFile(presentData);
204
205 if (presentSuccess) {
206 int low, high, found;
207 std::getline(presentFile, line);
208 found = sscanf(line.c_str(), "%d-%d", &low, &high);
209 if (found == 1)
210 return 1;
211 if (found == 2)
212 return high - low + 1;
213 }
214
215 std::string procdata;
216 if (!File::ReadFileToString(true, Path(procfile), procdata))
217 return 1;
218 std::istringstream file(procdata);
219
220 while (std::getline(file, line))
221 {
222 if (line.find(marker) != std::string::npos)
223 ++cores;
224 }
225
226 return cores;
227 }
228 #endif
229
230 CPUInfo cpu_info;
231
CPUInfo()232 CPUInfo::CPUInfo() {
233 Detect();
234 }
235
236 // Detects the various cpu features
Detect()237 void CPUInfo::Detect()
238 {
239 // Set some defaults here
240 HTT = false;
241 #if PPSSPP_ARCH(ARM64)
242 OS64bit = true;
243 CPU64bit = true;
244 Mode64bit = true;
245 #else
246 OS64bit = false;
247 CPU64bit = false;
248 Mode64bit = false;
249 #endif
250 vendor = VENDOR_ARM;
251 logical_cpu_count = 1;
252
253 // Get the information about the CPU
254 #if !PPSSPP_PLATFORM(LINUX)
255 bool isVFP3 = false;
256 bool isVFP4 = false;
257 #if PPSSPP_PLATFORM(IOS) || PPSSPP_PLATFORM(MAC)
258 #if PPSSPP_PLATFORM(IOS)
259 isVFP3 = true;
260 // Check for swift arch (VFP4)
261 #ifdef __ARM_ARCH_7S__
262 isVFP4 = true;
263 #endif
264 #endif // PPSSPP_PLATFORM(IOS)
265 size_t sz = 0x41; // char brand_string[0x41]
266 if (sysctlbyname("machdep.cpu.brand_string", brand_string, &sz, nullptr, 0) != 0) {
267 strcpy(brand_string, "Unknown");
268 }
269 int num = 0;
270 sz = sizeof(num);
271 if (sysctlbyname("hw.physicalcpu_max", &num, &sz, nullptr, 0) == 0) {
272 num_cores = num;
273 sz = sizeof(num);
274 if (sysctlbyname("hw.logicalcpu_max", &num, &sz, nullptr, 0) == 0) {
275 logical_cpu_count = num / num_cores;
276 }
277 }
278 #elif PPSSPP_PLATFORM(WINDOWS)
279 truncate_cpy(brand_string, GetCPUBrandString().c_str());
280 isVFP3 = true;
281 isVFP4 = false;
282 SYSTEM_INFO sysInfo;
283 GetSystemInfo(&sysInfo);
284 num_cores = sysInfo.dwNumberOfProcessors;
285 #else // !PPSSPP_PLATFORM(IOS) && !PPSSPP_PLATFORM(MAC) && !PPSSPP_PLATFORM(WINDOWS)
286 strcpy(brand_string, "Unknown");
287 num_cores = 1;
288 #endif
289 truncate_cpy(cpu_string, brand_string);
290 // Hardcode this for now
291 bSwp = true;
292 bHalf = true;
293 bThumb = false;
294 bFastMult = true;
295 bVFP = true;
296 bEDSP = true;
297 bThumbEE = isVFP3;
298 bNEON = isVFP3;
299 bVFPv3 = isVFP3;
300 bTLS = true;
301 bVFPv4 = isVFP4;
302 bIDIVa = isVFP4;
303 bIDIVt = isVFP4;
304 bFP = false;
305 bASIMD = false;
306 #else // PPSSPP_PLATFORM(LINUX)
307 truncate_cpy(cpu_string, GetCPUString().c_str());
308 truncate_cpy(brand_string, GetCPUBrandString().c_str());
309
310 bSwp = CheckCPUFeature("swp");
311 bHalf = CheckCPUFeature("half");
312 bThumb = CheckCPUFeature("thumb");
313 bFastMult = CheckCPUFeature("fastmult");
314 bVFP = CheckCPUFeature("vfp");
315 bEDSP = CheckCPUFeature("edsp");
316 bThumbEE = CheckCPUFeature("thumbee");
317 bNEON = CheckCPUFeature("neon");
318 bVFPv3 = CheckCPUFeature("vfpv3");
319 bTLS = CheckCPUFeature("tls");
320 bVFPv4 = CheckCPUFeature("vfpv4");
321 bIDIVa = CheckCPUFeature("idiva");
322 bIDIVt = CheckCPUFeature("idivt");
323 // Qualcomm Krait supports IDIVA but it doesn't report it. Check for krait (0x4D = Plus, 0x6F = Pro).
324 unsigned short CPUPart = GetCPUPart();
325 if (GetCPUImplementer() == 0x51 && (CPUPart == 0x4D || CPUPart == 0x6F))
326 bIDIVa = bIDIVt = true;
327 // These two require ARMv8 or higher
328 bFP = CheckCPUFeature("fp");
329 bASIMD = CheckCPUFeature("asimd");
330 num_cores = GetCoreCount();
331 #endif
332 #if PPSSPP_ARCH(ARM64)
333 // Whether the above detection failed or not, on ARM64 we do have ASIMD/NEON.
334 bNEON = true;
335 bASIMD = true;
336 #endif
337 }
338
339 // Turn the cpu info into a string we can show
Summarize()340 std::string CPUInfo::Summarize()
341 {
342 std::string sum;
343 if (num_cores == 1)
344 sum = StringFromFormat("%s, %d core", cpu_string, num_cores);
345 else
346 sum = StringFromFormat("%s, %d cores", cpu_string, num_cores);
347 if (bSwp) sum += ", SWP";
348 if (bHalf) sum += ", Half";
349 if (bThumb) sum += ", Thumb";
350 if (bFastMult) sum += ", FastMult";
351 if (bEDSP) sum += ", EDSP";
352 if (bThumbEE) sum += ", ThumbEE";
353 if (bTLS) sum += ", TLS";
354 if (bVFP) sum += ", VFP";
355 if (bVFPv3) sum += ", VFPv3";
356 if (bVFPv4) sum += ", VFPv4";
357 if (bNEON) sum += ", NEON";
358 if (bIDIVa) sum += ", IDIVa";
359 if (bIDIVt) sum += ", IDIVt";
360 if (CPU64bit) sum += ", 64-bit";
361
362 return sum;
363 }
364
365 #endif // PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64)
366