CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/ArmCPUDetect.cpp
Views: 1401
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
#if PPSSPP_ARCH(ARM)
30
#include "ext/cpu_features/include/cpuinfo_arm.h"
31
32
#if defined(CPU_FEATURES_OS_LINUX)
33
#define USE_CPU_FEATURES 1
34
#endif
35
#elif PPSSPP_ARCH(ARM64)
36
#include "ext/cpu_features/include/cpuinfo_aarch64.h"
37
38
#if defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID) || defined(CPU_FEATURES_OS_WINDOWS)
39
#define USE_CPU_FEATURES 1
40
#endif
41
#endif
42
43
#include <cstring>
44
#include <ctype.h>
45
46
#include "Common/CommonTypes.h"
47
#include "Common/CPUDetect.h"
48
#include "Common/StringUtils.h"
49
#include "Common/File/FileUtil.h"
50
#include "Common/Data/Encoding/Utf8.h"
51
52
#if PPSSPP_PLATFORM(WINDOWS)
53
#if PPSSPP_PLATFORM(UWP)
54
// TODO: Maybe we can move the implementation here?
55
std::string GetCPUBrandString();
56
#else
57
// No CPUID on ARM, so we'll have to read the registry
58
#include "Common/CommonWindows.h"
59
std::string GetCPUBrandString() {
60
std::string cpu_string;
61
62
HKEY key;
63
LSTATUS result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_READ, &key);
64
if (result == ERROR_SUCCESS) {
65
DWORD size = 0;
66
DWORD type = REG_SZ;
67
RegQueryValueEx(key, L"ProcessorNameString", NULL, &type, NULL, &size);
68
LPBYTE buff = (LPBYTE)malloc(size);
69
if (buff != NULL) {
70
RegQueryValueEx(key, L"ProcessorNameString", NULL, &type, buff, &size);
71
cpu_string = ConvertWStringToUTF8((wchar_t*)buff);
72
free(buff);
73
}
74
RegCloseKey(key);
75
}
76
77
if (cpu_string.empty())
78
return "Unknown";
79
else
80
return cpu_string;
81
}
82
#endif
83
#endif
84
85
// Only Linux platforms have /proc/cpuinfo
86
#if PPSSPP_PLATFORM(LINUX)
87
const char procfile[] = "/proc/cpuinfo";
88
// https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-devices-system-cpu
89
const char syscpupresentfile[] = "/sys/devices/system/cpu/present";
90
91
std::string GetCPUString() {
92
std::string procdata;
93
bool readSuccess = File::ReadSysTextFileToString(Path(procfile), &procdata);
94
std::istringstream file(procdata);
95
std::string cpu_string;
96
97
if (readSuccess) {
98
std::string line, marker = "Hardware\t: ";
99
while (std::getline(file, line)) {
100
if (line.find(marker) != std::string::npos) {
101
cpu_string = line.substr(marker.length());
102
}
103
}
104
}
105
106
if (cpu_string.empty())
107
cpu_string = "Unknown";
108
else if (cpu_string.back() == '\n')
109
cpu_string.pop_back(); // Drop the new-line character
110
111
return cpu_string;
112
}
113
114
std::string GetCPUBrandString() {
115
std::string procdata;
116
bool readSuccess = File::ReadSysTextFileToString(Path(procfile), &procdata);
117
std::istringstream file(procdata);
118
std::string brand_string;
119
120
if (readSuccess) {
121
std::string line, marker = "Processor\t: ";
122
while (std::getline(file, line)) {
123
if (line.find(marker) != std::string::npos) {
124
brand_string = line.substr(marker.length());
125
if (brand_string.length() != 0 && !isdigit(brand_string[0])) {
126
break;
127
}
128
}
129
}
130
}
131
132
if (brand_string.empty())
133
brand_string = "Unknown";
134
else if (brand_string.back() == '\n')
135
brand_string.pop_back(); // Drop the new-line character
136
137
return brand_string;
138
}
139
140
unsigned char GetCPUImplementer()
141
{
142
std::string line, marker = "CPU implementer\t: ";
143
unsigned char implementer = 0;
144
145
std::string procdata;
146
if (!File::ReadSysTextFileToString(Path(procfile), &procdata))
147
return 0;
148
std::istringstream file(procdata);
149
150
while (std::getline(file, line))
151
{
152
if (line.find(marker) != std::string::npos)
153
{
154
line = line.substr(marker.length());
155
sscanf(line.c_str(), "0x%02hhx", &implementer);
156
break;
157
}
158
}
159
160
return implementer;
161
}
162
163
unsigned short GetCPUPart()
164
{
165
std::string line, marker = "CPU part\t: ";
166
unsigned short part = 0;
167
168
std::string procdata;
169
if (!File::ReadSysTextFileToString(Path(procfile), &procdata))
170
return 0;
171
std::istringstream file(procdata);
172
173
while (std::getline(file, line))
174
{
175
if (line.find(marker) != std::string::npos)
176
{
177
line = line.substr(marker.length());
178
sscanf(line.c_str(), "0x%03hx", &part);
179
break;
180
}
181
}
182
183
return part;
184
}
185
186
bool CheckCPUFeature(const std::string& feature)
187
{
188
std::string line, marker = "Features\t: ";
189
190
std::string procdata;
191
if (!File::ReadSysTextFileToString(Path(procfile), &procdata))
192
return false;
193
std::istringstream file(procdata);
194
while (std::getline(file, line))
195
{
196
if (line.find(marker) != std::string::npos)
197
{
198
std::stringstream line_stream(line);
199
std::string token;
200
while (std::getline(line_stream, token, ' '))
201
{
202
if (token == feature)
203
return true;
204
}
205
}
206
}
207
208
return false;
209
}
210
211
int GetCoreCount()
212
{
213
std::string line, marker = "processor\t: ";
214
int cores = 1;
215
216
std::string presentData;
217
bool presentSuccess = File::ReadSysTextFileToString(Path(syscpupresentfile), &presentData);
218
std::istringstream presentFile(presentData);
219
220
if (presentSuccess) {
221
int low, high, found;
222
std::getline(presentFile, line);
223
found = sscanf(line.c_str(), "%d-%d", &low, &high);
224
if (found == 1)
225
return 1;
226
if (found == 2)
227
return high - low + 1;
228
}
229
230
std::string procdata;
231
if (!File::ReadSysTextFileToString(Path(procfile), &procdata))
232
return 1;
233
std::istringstream file(procdata);
234
235
while (std::getline(file, line))
236
{
237
if (line.find(marker) != std::string::npos)
238
++cores;
239
}
240
241
return cores;
242
}
243
#endif
244
245
CPUInfo cpu_info;
246
247
CPUInfo::CPUInfo() {
248
Detect();
249
}
250
251
// Detects the various cpu features
252
void CPUInfo::Detect()
253
{
254
// Set some defaults here
255
HTT = false;
256
#if PPSSPP_ARCH(ARM64)
257
OS64bit = true;
258
CPU64bit = true;
259
Mode64bit = true;
260
#else
261
OS64bit = false;
262
CPU64bit = false;
263
Mode64bit = false;
264
#endif
265
vendor = VENDOR_ARM;
266
logical_cpu_count = 1;
267
268
// Get the information about the CPU
269
#if !PPSSPP_PLATFORM(LINUX)
270
bool isVFP3 = false;
271
bool isVFP4 = false;
272
#if PPSSPP_PLATFORM(IOS) || PPSSPP_PLATFORM(MAC)
273
#if PPSSPP_PLATFORM(IOS)
274
isVFP3 = true;
275
// Check for swift arch (VFP4)
276
#ifdef __ARM_ARCH_7S__
277
isVFP4 = true;
278
#endif
279
#endif // PPSSPP_PLATFORM(IOS)
280
size_t sz = 0x41; // char brand_string[0x41]
281
if (sysctlbyname("machdep.cpu.brand_string", brand_string, &sz, nullptr, 0) != 0) {
282
strcpy(brand_string, "Unknown");
283
}
284
int num = 0;
285
sz = sizeof(num);
286
if (sysctlbyname("hw.physicalcpu_max", &num, &sz, nullptr, 0) == 0) {
287
num_cores = num;
288
sz = sizeof(num);
289
if (sysctlbyname("hw.logicalcpu_max", &num, &sz, nullptr, 0) == 0) {
290
logical_cpu_count = num / num_cores;
291
}
292
}
293
#elif PPSSPP_PLATFORM(WINDOWS)
294
truncate_cpy(brand_string, GetCPUBrandString().c_str());
295
isVFP3 = true;
296
isVFP4 = false;
297
SYSTEM_INFO sysInfo;
298
GetSystemInfo(&sysInfo);
299
num_cores = sysInfo.dwNumberOfProcessors;
300
#else // !PPSSPP_PLATFORM(IOS) && !PPSSPP_PLATFORM(MAC) && !PPSSPP_PLATFORM(WINDOWS)
301
strcpy(brand_string, "Unknown");
302
num_cores = 1;
303
#endif
304
truncate_cpy(cpu_string, brand_string);
305
// Hardcode this for now
306
bSwp = true;
307
bHalf = true;
308
bThumb = false;
309
bFastMult = true;
310
bVFP = true;
311
bEDSP = true;
312
bThumbEE = isVFP3;
313
bNEON = isVFP3;
314
bVFPv3 = isVFP3;
315
bTLS = true;
316
bVFPv4 = isVFP4;
317
bIDIVa = isVFP4;
318
bIDIVt = isVFP4;
319
bFP = false;
320
bASIMD = false;
321
#else // PPSSPP_PLATFORM(LINUX)
322
truncate_cpy(cpu_string, GetCPUString().c_str());
323
truncate_cpy(brand_string, GetCPUBrandString().c_str());
324
325
bSwp = CheckCPUFeature("swp");
326
bHalf = CheckCPUFeature("half");
327
bThumb = CheckCPUFeature("thumb");
328
bFastMult = CheckCPUFeature("fastmult");
329
bVFP = CheckCPUFeature("vfp");
330
bEDSP = CheckCPUFeature("edsp");
331
bThumbEE = CheckCPUFeature("thumbee");
332
bNEON = CheckCPUFeature("neon");
333
bVFPv3 = CheckCPUFeature("vfpv3");
334
bTLS = CheckCPUFeature("tls");
335
bVFPv4 = CheckCPUFeature("vfpv4");
336
bIDIVa = CheckCPUFeature("idiva");
337
bIDIVt = CheckCPUFeature("idivt");
338
// Qualcomm Krait supports IDIVA but it doesn't report it. Check for krait (0x4D = Plus, 0x6F = Pro).
339
unsigned short CPUPart = GetCPUPart();
340
if (GetCPUImplementer() == 0x51 && (CPUPart == 0x4D || CPUPart == 0x6F))
341
bIDIVa = bIDIVt = true;
342
// Vero4k supports NEON but doesn't report it. Check for Arm Cortex-A53.
343
if (GetCPUImplementer() == 0x41 && CPUPart == 0xd03)
344
bNEON = true;
345
// These two require ARMv8 or higher
346
bFP = CheckCPUFeature("fp");
347
bASIMD = CheckCPUFeature("asimd");
348
num_cores = GetCoreCount();
349
#endif
350
#if PPSSPP_ARCH(ARM64)
351
// Whether the above detection failed or not, on ARM64 we do have ASIMD/NEON.
352
bNEON = true;
353
bASIMD = true;
354
#endif
355
356
#if PPSSPP_ARCH(ARM) && defined(USE_CPU_FEATURES)
357
cpu_features::ArmInfo info = cpu_features::GetArmInfo();
358
bSwp = info.features.swp;
359
bHalf = info.features.half;
360
bThumb = info.features.thumb;
361
bFastMult = info.features.fastmult;
362
bEDSP = info.features.edsp;
363
bThumbEE = info.features.thumbee;
364
bNEON = info.features.neon;
365
bTLS = info.features.tls;
366
bVFP = info.features.vfp;
367
bVFPv3 = info.features.vfpv3;
368
bVFPv4 = info.features.vfpv4;
369
bIDIVa = info.features.idiva;
370
bIDIVt = info.features.idivt;
371
#endif
372
#if PPSSPP_ARCH(ARM64) && defined(USE_CPU_FEATURES)
373
cpu_features::Aarch64Info info = cpu_features::GetAarch64Info();
374
bFP = info.features.fp;
375
bASIMD = info.features.asimd;
376
bSVE = info.features.sve;
377
bSVE2 = info.features.sve2;
378
bFRINT = info.features.frint;
379
#endif
380
}
381
382
std::vector<std::string> CPUInfo::Features() {
383
std::vector<std::string> features;
384
385
struct Flag {
386
bool &flag;
387
const char *str;
388
};
389
const Flag list[] = {
390
{ bSwp, "SWP" },
391
{ bHalf, "Half" },
392
{ bThumb, "Thumb" },
393
{ bFastMult, "FastMult" },
394
{ bEDSP, "EDSP" },
395
{ bThumbEE, "ThumbEE" },
396
{ bTLS, "TLS" },
397
{ bVFP, "VFP" },
398
{ bVFPv3, "VFPv3" },
399
{ bVFPv4, "VFPv4" },
400
{ bNEON, "NEON" },
401
{ bIDIVa, "IDIVa" },
402
{ bIDIVt, "IDIVt" },
403
{ bFRINT, "FRINT" },
404
{ bSVE, "SVE" },
405
{ bSVE2, "SVE2" },
406
{ CPU64bit, "64-bit" },
407
};
408
409
for (auto &item : list) {
410
if (item.flag) {
411
features.push_back(item.str);
412
}
413
}
414
415
return features;
416
}
417
418
// Turn the cpu info into a string we can show
419
std::string CPUInfo::Summarize() {
420
std::string sum;
421
if (num_cores == 1)
422
sum = StringFromFormat("%s, %d core", cpu_string, num_cores);
423
else
424
sum = StringFromFormat("%s, %d cores", cpu_string, num_cores);
425
426
auto features = Features();
427
for (std::string &feature : features) {
428
sum += ", " + feature;
429
}
430
return sum;
431
}
432
433
#endif // PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64)
434
435