2020-12-18 10:55:54 +01:00
# include "util/sysinfo.hpp"
# include "Utilities/StrFmt.h"
# include "Utilities/File.h"
2021-09-23 20:12:06 +02:00
# include "Emu/vfs_config.h"
2020-12-18 10:55:54 +01:00
# include "Utilities/Thread.h"
2017-07-18 14:21:29 +02:00
2024-08-23 00:52:59 +02:00
# if defined(ARCH_ARM64)
# include "Emu/CPU/Backends/AArch64/AArch64Common.h"
# endif
2017-07-18 14:21:29 +02:00
# ifdef _WIN32
# include "windows.h"
2019-04-19 14:05:47 +02:00
# include "sysinfoapi.h"
# include "subauth.h"
# include "stringapiset.h"
2017-07-18 14:21:29 +02:00
# else
# include <unistd.h>
2022-01-10 14:15:04 +01:00
# include <sys/resource.h>
2022-01-11 23:17:26 +01:00
# ifndef __APPLE__
2019-04-19 14:05:47 +02:00
# include <sys/utsname.h>
# include <errno.h>
2017-07-18 14:21:29 +02:00
# endif
2022-01-11 23:17:26 +01:00
# endif
2017-07-18 14:21:29 +02:00
2020-12-18 15:43:34 +01:00
# include "util/asm.hpp"
2021-12-30 17:39:18 +01:00
# include "util/fence.hpp"
2020-12-18 15:43:34 +01:00
2023-07-11 20:40:30 +02:00
# if defined(_M_X64) && defined(_MSC_VER)
2021-12-30 17:39:18 +01:00
extern " C " u64 _xgetbv ( u32 ) ;
2020-12-21 15:12:05 +01:00
# endif
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
static inline std : : array < u32 , 4 > get_cpuid ( u32 func , u32 subfunc )
2020-12-18 10:55:54 +01:00
{
int regs [ 4 ] ;
# ifdef _MSC_VER
__cpuidex ( regs , func , subfunc ) ;
# else
__asm__ volatile ( " cpuid " : " =a " ( regs [ 0 ] ) , " =b " ( regs [ 1 ] ) , " =c " ( regs [ 2 ] ) , " =d " ( regs [ 3 ] ) : " a " ( func ) , " c " ( subfunc ) ) ;
# endif
return { 0u + regs [ 0 ] , 0u + regs [ 1 ] , 0u + regs [ 2 ] , 0u + regs [ 3 ] } ;
}
2021-12-30 17:39:18 +01:00
static inline u64 get_xgetbv ( u32 xcr )
2020-12-18 10:55:54 +01:00
{
# ifdef _MSC_VER
return _xgetbv ( xcr ) ;
# else
u32 eax , edx ;
__asm__ volatile ( " xgetbv " : " =a " ( eax ) , " =d " ( edx ) : " c " ( xcr ) ) ;
return eax | ( u64 ( edx ) < < 32 ) ;
# endif
}
2021-12-30 17:39:18 +01:00
# endif
2020-12-18 10:55:54 +01:00
2022-01-11 23:17:26 +01:00
# ifdef __APPLE__
// sysinfo_darwin.mm
namespace Darwin_Version
{
extern int getNSmajorVersion ( ) ;
extern int getNSminorVersion ( ) ;
extern int getNSpatchVersion ( ) ;
}
2022-03-05 17:32:35 +01:00
namespace Darwin_ProcessInfo
{
extern bool getLowPowerModeEnabled ( ) ;
}
2022-01-11 23:17:26 +01:00
# endif
2024-09-08 02:46:12 +02:00
namespace utils
2024-09-03 00:57:23 +02:00
{
2024-09-08 02:46:12 +02:00
# ifdef _WIN32
// Alternative way to read OS version using the registry.
static std : : string get_fallback_windows_version ( )
{
// Some helpers for sanity
const auto read_reg_dword = [ ] ( HKEY hKey , std : : string_view value_name ) - > std : : pair < bool , DWORD >
{
DWORD val ;
DWORD len = sizeof ( val ) ;
if ( ERROR_SUCCESS ! = RegQueryValueExA ( hKey , value_name . data ( ) , nullptr , nullptr , reinterpret_cast < LPBYTE > ( & val ) , & len ) )
{
return { false , 0 } ;
}
return { true , val } ;
} ;
const auto read_reg_sz = [ ] ( HKEY hKey , std : : string_view value_name ) - > std : : pair < bool , std : : string >
{
2024-09-08 14:21:14 +02:00
constexpr usz MAX_SZ_LEN = 255 ;
char sz [ MAX_SZ_LEN + 1 ] ;
DWORD sz_len = MAX_SZ_LEN ;
// Safety; null terminate
sz [ 0 ] = 0 ;
sz [ MAX_SZ_LEN ] = 0 ;
// Read string
2024-09-08 02:46:12 +02:00
if ( ERROR_SUCCESS ! = RegQueryValueExA ( hKey , value_name . data ( ) , nullptr , nullptr , reinterpret_cast < LPBYTE > ( sz ) , & sz_len ) )
{
return { false , " " } ;
}
2024-09-08 14:21:14 +02:00
// Safety, force null terminator
if ( sz_len < MAX_SZ_LEN )
2024-09-08 02:46:12 +02:00
{
sz [ sz_len ] = 0 ;
}
return { true , sz } ;
} ;
HKEY hKey ;
if ( ERROR_SUCCESS ! = RegOpenKeyExA ( HKEY_LOCAL_MACHINE , " SOFTWARE \\ Microsoft \\ Windows NT \\ CurrentVersion " , 0 , KEY_READ , & hKey ) )
{
return " Unknown Windows " ;
}
2024-09-08 14:21:14 +02:00
// ProductName (SZ) - Actual windows install name e.g Windows 10 Pro)
// CurrentMajorVersionNumber (DWORD) - e.g 10 for windows 10, 11 for windows 11
// CurrentMinorVersionNumber (DWORD) - usually 0 for newer windows, pairs with major version
// CurrentBuildNumber (SZ) - Windows build number, e.g 19045, used to identify different releases like 23H2, 24H2, etc
// CurrentVersion (SZ) - NT kernel version, e.g 6.3 for Windows 10
2024-09-08 02:46:12 +02:00
const auto [ product_valid , product_name ] = read_reg_sz ( hKey , " ProductName " ) ;
if ( ! product_valid )
{
2024-09-08 14:21:14 +02:00
RegCloseKey ( hKey ) ;
2024-09-08 02:46:12 +02:00
return " Unknown Windows " ;
}
const auto [ check_major , version_major ] = read_reg_dword ( hKey , " CurrentMajorVersionNumber " ) ;
const auto [ check_minor , version_minor ] = read_reg_dword ( hKey , " CurrentMinorVersionNumber " ) ;
const auto [ check_build_no , build_no ] = read_reg_sz ( hKey , " CurrentBuildNumber " ) ;
const auto [ check_nt_ver , nt_ver ] = read_reg_sz ( hKey , " CurrentVersion " ) ;
// Close the registry key
RegCloseKey ( hKey ) ;
std : : string version_id = " Unknown " ;
if ( check_major & & check_minor & & check_build_no )
{
version_id = fmt : : format ( " %u.%u.%s " , version_major , version_minor , build_no ) ;
if ( check_nt_ver )
{
version_id + = " NT " + nt_ver ;
}
}
return fmt : : format ( " Operating system: %s, Version %s " , product_name , version_id ) ;
}
2024-09-03 00:57:23 +02:00
# endif
2024-09-08 02:46:12 +02:00
}
2024-09-03 00:57:23 +02:00
2017-11-09 18:33:18 +01:00
bool utils : : has_ssse3 ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2017-11-09 18:33:18 +01:00
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x1 & & get_cpuid ( 1 , 0 ) [ 2 ] & 0x200 ;
return g_value ;
2021-12-30 17:39:18 +01:00
# else
return false ;
# endif
2017-11-09 18:33:18 +01:00
}
2018-04-09 16:45:37 +02:00
bool utils : : has_sse41 ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2018-04-09 16:45:37 +02:00
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x1 & & get_cpuid ( 1 , 0 ) [ 2 ] & 0x80000 ;
return g_value ;
2021-12-30 17:39:18 +01:00
# else
return false ;
# endif
2018-04-09 16:45:37 +02:00
}
2017-11-09 18:33:18 +01:00
bool utils : : has_avx ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2018-03-23 23:08:13 +01:00
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x1 & & get_cpuid ( 1 , 0 ) [ 2 ] & 0x10000000 & & ( get_cpuid ( 1 , 0 ) [ 2 ] & 0x0C000000 ) = = 0x0C000000 & & ( get_xgetbv ( 0 ) & 0x6 ) = = 0x6 ;
2017-11-09 18:33:18 +01:00
return g_value ;
2021-12-30 17:39:18 +01:00
# else
return false ;
# endif
2017-11-09 18:33:18 +01:00
}
2017-12-16 01:19:21 +01:00
bool utils : : has_avx2 ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2018-03-23 23:08:13 +01:00
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x7 & & get_cpuid ( 7 , 0 ) [ 1 ] & 0x20 & & ( get_cpuid ( 1 , 0 ) [ 2 ] & 0x0C000000 ) = = 0x0C000000 & & ( get_xgetbv ( 0 ) & 0x6 ) = = 0x6 ;
2017-12-16 01:19:21 +01:00
return g_value ;
2021-12-30 17:39:18 +01:00
# else
return false ;
# endif
2017-12-16 01:19:21 +01:00
}
2017-11-09 18:33:18 +01:00
bool utils : : has_rtm ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2018-06-13 13:54:16 +02:00
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x7 & & ( get_cpuid ( 7 , 0 ) [ 1 ] & 0x800 ) = = 0x800 ;
return g_value ;
2021-12-30 17:39:18 +01:00
# elif defined(ARCH_ARM64)
return false ;
# endif
2018-06-13 13:54:16 +02:00
}
2019-06-01 05:34:46 +02:00
bool utils : : has_tsx_force_abort ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2019-06-01 05:34:46 +02:00
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x7 & & ( get_cpuid ( 7 , 0 ) [ 3 ] & 0x2000 ) = = 0x2000 ;
return g_value ;
2021-12-30 17:39:18 +01:00
# else
return false ;
# endif
2019-06-01 05:34:46 +02:00
}
2021-11-04 17:44:25 +01:00
bool utils : : has_rtm_always_abort ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2021-11-04 17:44:25 +01:00
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x7 & & ( get_cpuid ( 7 , 0 ) [ 3 ] & 0x800 ) = = 0x800 ;
return g_value ;
2021-12-30 17:39:18 +01:00
# else
return false ;
# endif
2021-11-04 17:44:25 +01:00
}
2018-06-13 13:54:16 +02:00
bool utils : : has_mpx ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2018-06-13 13:54:16 +02:00
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x7 & & ( get_cpuid ( 7 , 0 ) [ 1 ] & 0x4000 ) = = 0x4000 ;
2017-11-09 18:33:18 +01:00
return g_value ;
2021-12-30 17:39:18 +01:00
# else
return false ;
# endif
2017-11-09 18:33:18 +01:00
}
2020-03-10 17:21:00 +01:00
bool utils : : has_avx512 ( )
2017-12-16 01:19:21 +01:00
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2017-12-16 01:19:21 +01:00
// Check AVX512F, AVX512CD, AVX512DQ, AVX512BW, AVX512VL extensions (Skylake-X level support)
2020-03-10 17:21:00 +01:00
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x7 & & ( get_cpuid ( 7 , 0 ) [ 1 ] & 0xd0030000 ) = = 0xd0030000 & & ( get_cpuid ( 1 , 0 ) [ 2 ] & 0x0C000000 ) = = 0x0C000000 & & ( get_xgetbv ( 0 ) & 0xe6 ) = = 0xe6 ;
2017-12-16 01:19:21 +01:00
return g_value ;
2021-12-30 17:39:18 +01:00
# else
return false ;
# endif
2017-12-16 01:19:21 +01:00
}
2020-08-06 21:37:10 +02:00
bool utils : : has_avx512_icl ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2020-08-06 21:37:10 +02:00
// Check AVX512IFMA, AVX512VBMI, AVX512VBMI2, AVX512VPOPCNTDQ, AVX512BITALG, AVX512VNNI, AVX512VPCLMULQDQ, AVX512GFNI, AVX512VAES (Icelake-client level support)
static const bool g_value = has_avx512 ( ) & & ( get_cpuid ( 7 , 0 ) [ 1 ] & 0x00200000 ) = = 0x00200000 & & ( get_cpuid ( 7 , 0 ) [ 2 ] & 0x00005f42 ) = = 0x00005f42 ;
return g_value ;
2021-12-30 17:39:18 +01:00
# else
return false ;
# endif
}
bool utils : : has_avx512_vnni ( )
{
# if defined(ARCH_X64)
// Check AVX512VNNI
static const bool g_value = has_avx512 ( ) & & get_cpuid ( 7 , 0 ) [ 2 ] & 0x00000800 ;
return g_value ;
# else
return false ;
# endif
2020-08-06 21:37:10 +02:00
}
2023-07-28 05:55:20 +02:00
bool utils : : has_avx10 ( )
{
# if defined(ARCH_X64)
// Implies support for most AVX-512 instructions
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x7 & & get_cpuid ( 7 , 1 ) [ 3 ] & 0x80000 ;
return g_value ;
# else
return false ;
# endif
}
bool utils : : has_avx10_512 ( )
{
# if defined(ARCH_X64)
// AVX10 with 512 wide vectors
static const bool g_value = has_avx10 ( ) & & get_cpuid ( 24 , 0 ) [ 2 ] & 0x40000 ;
return g_value ;
# else
return false ;
# endif
}
u32 utils : : avx10_isa_version ( )
{
# if defined(ARCH_X64)
// 8bit value
static const u32 g_value = [ ] ( )
{
u32 isa_version = 0 ;
if ( has_avx10 ( ) )
{
isa_version = get_cpuid ( 24 , 0 ) [ 2 ] & 0x000ff ;
}
return isa_version ;
} ( ) ;
return g_value ;
# else
return 0 ;
# endif
}
bool utils : : has_avx512_256 ( )
{
# if defined(ARCH_X64)
// Either AVX10 or AVX512 implies support for 256-bit length AVX-512 SKL-X tier instructions
static const bool g_value = ( has_avx512 ( ) | | has_avx10 ( ) ) ;
return g_value ;
# else
return false ;
# endif
}
bool utils : : has_avx512_icl_256 ( )
{
# if defined(ARCH_X64)
// Check for AVX512_ICL or check for AVX10, together with GFNI, VAES, and VPCLMULQDQ, implies support for the same instructions that AVX-512_icl does at 256 bit length
static const bool g_value = ( has_avx512_icl ( ) | | ( has_avx10 ( ) & & get_cpuid ( 7 , 0 ) [ 2 ] & 0x00000700 ) ) ;
return g_value ;
# else
return false ;
# endif
}
2018-01-16 12:32:57 +01:00
bool utils : : has_xop ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2018-01-16 12:32:57 +01:00
static const bool g_value = has_avx ( ) & & get_cpuid ( 0x80000001 , 0 ) [ 2 ] & 0x800 ;
return g_value ;
2021-12-30 17:39:18 +01:00
# else
return false ;
# endif
2018-01-16 12:32:57 +01:00
}
2019-11-14 17:09:34 +01:00
bool utils : : has_clwb ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2019-11-14 17:09:34 +01:00
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x7 & & ( get_cpuid ( 7 , 0 ) [ 1 ] & 0x1000000 ) = = 0x1000000 ;
return g_value ;
2021-12-30 17:39:18 +01:00
# else
return false ;
# endif
2019-11-14 17:09:34 +01:00
}
2020-04-04 20:12:06 +02:00
bool utils : : has_invariant_tsc ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2020-04-04 20:12:06 +02:00
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x7 & & ( get_cpuid ( 0x80000007 , 0 ) [ 3 ] & 0x100 ) = = 0x100 ;
return g_value ;
2021-12-30 17:39:18 +01:00
# elif defined(ARCH_ARM64)
return true ;
# endif
2020-04-04 20:12:06 +02:00
}
bool utils : : has_fma3 ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2020-04-04 20:12:06 +02:00
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x1 & & get_cpuid ( 1 , 0 ) [ 2 ] & 0x1000 ;
return g_value ;
2021-12-30 17:39:18 +01:00
# elif defined(ARCH_ARM64)
return true ;
# endif
2020-04-04 20:12:06 +02:00
}
bool utils : : has_fma4 ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2020-04-04 20:12:06 +02:00
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x7 & & ( get_cpuid ( 0x80000001 , 0 ) [ 2 ] & 0x10000 ) = = 0x10000 ;
return g_value ;
2021-12-30 17:39:18 +01:00
# else
return false ;
# endif
2020-04-04 20:12:06 +02:00
}
2022-09-30 23:39:24 +02:00
// The Zen4 based CPUs support VPERMI2B/VPERMT2B in a single uop.
// Current Intel cpus (as of 2022) need 3 uops to execute these instructions.
// Check for SSE4A (which intel doesn't doesn't support) as well as VBMI.
bool utils : : has_fast_vperm2b ( )
{
# if defined(ARCH_X64)
2023-08-05 10:49:30 +02:00
static const bool g_value = has_avx512 ( ) & & ( get_cpuid ( 7 , 0 ) [ 2 ] & 0x2 ) = = 0x2 & & get_cpuid ( 0 , 0 ) [ 0 ] > = 0x7 & & ( get_cpuid ( 0x80000001 , 0 ) [ 2 ] & 0x40 ) = = 0x40 ;
2022-09-30 23:39:24 +02:00
return g_value ;
# else
return false ;
# endif
}
2021-10-30 05:55:47 +02:00
bool utils : : has_erms ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2021-10-30 05:55:47 +02:00
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x7 & & ( get_cpuid ( 7 , 0 ) [ 1 ] & 0x200 ) = = 0x200 ;
return g_value ;
2021-12-30 17:39:18 +01:00
# else
return false ;
# endif
2021-10-30 05:55:47 +02:00
}
bool utils : : has_fsrm ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2021-10-30 05:55:47 +02:00
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x7 & & ( get_cpuid ( 7 , 0 ) [ 3 ] & 0x10 ) = = 0x10 ;
return g_value ;
2021-12-30 17:39:18 +01:00
# else
return false ;
# endif
2021-10-30 05:55:47 +02:00
}
2023-08-05 10:49:30 +02:00
bool utils : : has_waitx ( )
{
# if defined(ARCH_X64)
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x7 & & ( get_cpuid ( 0x80000001 , 0 ) [ 2 ] & 0x20000000 ) = = 0x20000000 ;
return g_value ;
# else
return false ;
# endif
}
bool utils : : has_waitpkg ( )
{
# if defined(ARCH_X64)
static const bool g_value = get_cpuid ( 0 , 0 ) [ 0 ] > = 0x7 & & ( get_cpuid ( 7 , 0 ) [ 2 ] & 0x20 ) = = 0x20 ;
return g_value ;
# else
return false ;
# endif
}
// User mode waits may be unfriendly to low thread CPUs
// Filter out systems with less than 8 threads for linux and less than 12 threads for other platforms
bool utils : : has_appropriate_um_wait ( )
{
# ifdef __linux__
static const bool g_value = ( has_waitx ( ) | | has_waitpkg ( ) ) & & ( get_thread_count ( ) > = 8 ) & & get_tsc_freq ( ) ;
return g_value ;
# else
static const bool g_value = ( has_waitx ( ) | | has_waitpkg ( ) ) & & ( get_thread_count ( ) > = 12 ) & & get_tsc_freq ( ) ;
return g_value ;
# endif
}
2023-08-16 15:16:49 +02:00
// Similar to the above function but allow execution if alternatives such as yield are not wanted
bool utils : : has_um_wait ( )
{
static const bool g_value = ( has_waitx ( ) | | has_waitpkg ( ) ) & & get_tsc_freq ( ) ;
return g_value ;
}
2021-10-30 05:55:47 +02:00
u32 utils : : get_rep_movsb_threshold ( )
{
static const u32 g_value = [ ] ( )
{
2021-12-30 17:39:18 +01:00
u32 thresh_value = umax ;
2021-10-30 05:55:47 +02:00
if ( has_fsrm ( ) )
{
thresh_value = 2047 ;
}
else if ( has_erms ( ) )
{
thresh_value = 4095 ;
}
return thresh_value ;
} ( ) ;
return g_value ;
}
2020-02-20 03:55:25 +01:00
std : : string utils : : get_cpu_brand ( )
2017-07-18 14:21:29 +02:00
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2024-08-23 00:52:59 +02:00
std : : string brand ;
2017-07-18 14:21:29 +02:00
if ( get_cpuid ( 0x80000000 , 0 ) [ 0 ] > = 0x80000004 )
{
for ( u32 i = 0 ; i < 3 ; i + + )
{
brand . append ( reinterpret_cast < const char * > ( get_cpuid ( 0x80000002 + i , 0 ) . data ( ) ) , 16 ) ;
}
}
else
{
brand = " Unknown CPU " ;
}
2018-09-16 11:46:40 +02:00
brand . erase ( brand . find_last_not_of ( ' \0 ' ) + 1 ) ;
2017-07-18 14:21:29 +02:00
brand . erase ( brand . find_last_not_of ( ' ' ) + 1 ) ;
2018-09-16 11:46:40 +02:00
brand . erase ( 0 , brand . find_first_not_of ( ' ' ) ) ;
2017-07-18 14:21:29 +02:00
while ( auto found = brand . find ( " " ) + 1 )
{
brand . erase ( brand . begin ( ) + found ) ;
}
2020-02-20 03:55:25 +01:00
return brand ;
2024-08-23 00:52:59 +02:00
# elif defined(ARCH_ARM64)
static const auto g_cpu_brand = aarch64 : : get_cpu_brand ( ) ;
return g_cpu_brand ;
# else
return " Unidentified CPU " ;
# endif
2020-02-20 03:55:25 +01:00
}
std : : string utils : : get_system_info ( )
{
std : : string result ;
const std : : string brand = get_cpu_brand ( ) ;
const u64 mem_total = get_total_memory ( ) ;
const u32 num_proc = get_thread_count ( ) ;
2017-07-18 14:21:29 +02:00
fmt : : append ( result , " %s | %d Threads | %.2f GiB RAM " , brand , num_proc , mem_total / ( 1024.0f * 1024 * 1024 ) ) ;
2017-07-18 19:03:47 +02:00
2020-04-06 17:23:45 +02:00
if ( const ullong tsc_freq = get_tsc_freq ( ) )
2020-04-04 20:12:06 +02:00
{
2020-04-09 14:31:34 +02:00
fmt : : append ( result , " | TSC: %.03fGHz " , tsc_freq / 1000000000. ) ;
2019-07-15 16:20:12 +02:00
}
else
{
fmt : : append ( result , " | TSC: Bad " ) ;
}
2017-07-18 19:03:47 +02:00
if ( has_avx ( ) )
{
2017-12-16 01:19:21 +01:00
result + = " | AVX " ;
2023-07-28 05:55:20 +02:00
if ( has_avx10 ( ) )
{
const u32 avx10_version = avx10_isa_version ( ) ;
fmt : : append ( result , " 10.%d " , avx10_version ) ;
if ( has_avx10_512 ( ) )
{
result + = " -512 " ;
}
else
{
result + = " -256 " ;
}
}
else if ( has_avx512 ( ) )
2017-12-16 01:19:21 +01:00
{
2020-08-06 21:37:10 +02:00
result + = " -512 " ;
2017-12-16 01:19:21 +01:00
2020-08-06 21:37:10 +02:00
if ( has_avx512_icl ( ) )
{
result + = ' + ' ;
}
}
else if ( has_avx2 ( ) )
2017-12-16 01:19:21 +01:00
{
result + = ' + ' ;
}
2018-01-16 12:32:57 +01:00
if ( has_xop ( ) )
{
result + = ' x ' ;
}
2017-07-18 19:03:47 +02:00
}
2020-04-04 20:12:06 +02:00
if ( has_fma3 ( ) | | has_fma4 ( ) )
{
result + = " | FMA " ;
2020-04-06 17:23:45 +02:00
2020-04-04 20:12:06 +02:00
if ( has_fma3 ( ) & & has_fma4 ( ) )
{
result + = " 3+4 " ;
}
else if ( has_fma3 ( ) )
{
result + = " 3 " ;
}
else if ( has_fma4 ( ) )
{
result + = " 4 " ;
}
}
2017-07-18 19:03:47 +02:00
if ( has_rtm ( ) )
{
result + = " | TSX " ;
2019-06-01 05:34:46 +02:00
if ( has_tsx_force_abort ( ) )
{
result + = " -FA " ;
}
2019-07-15 16:20:12 +02:00
2021-11-04 17:44:25 +01:00
if ( ! has_mpx ( ) | | has_tsx_force_abort ( ) )
2018-06-13 13:54:16 +02:00
{
result + = " disabled by default " ;
}
2017-07-18 19:03:47 +02:00
}
2021-11-04 17:44:25 +01:00
else if ( has_rtm_always_abort ( ) )
{
result + = " | TSX disabled via microcode " ;
}
2017-07-18 19:03:47 +02:00
2017-07-18 14:21:29 +02:00
return result ;
}
2019-02-16 14:05:13 +01:00
std : : string utils : : get_firmware_version ( )
{
2021-09-23 20:12:06 +02:00
const std : : string file_path = g_cfg_vfs . get_dev_flash ( ) + " vsh/etc/version.txt " ;
2021-03-05 15:18:17 +01:00
if ( fs : : file version_file { file_path } )
2019-02-16 14:05:13 +01:00
{
2021-03-05 15:18:17 +01:00
const std : : string version_str = version_file . to_string ( ) ;
std : : string_view version = version_str ;
2019-02-16 14:05:13 +01:00
// Extract version
2020-12-18 08:39:54 +01:00
const usz start = version . find_first_of ( ' : ' ) + 1 ;
const usz end = version . find_first_of ( ' : ' , start ) ;
2021-03-05 15:18:17 +01:00
if ( ! start | | end = = umax )
{
return { } ;
}
2019-02-16 14:05:13 +01:00
version = version . substr ( start , end - start ) ;
2023-04-18 20:07:02 +02:00
// Trim version (e.g. '04.8900' becomes '4.89')
2023-04-18 22:59:21 +02:00
usz trim_start = version . find_first_not_of ( ' 0 ' ) ;
2019-02-16 14:05:13 +01:00
2021-03-05 15:18:17 +01:00
if ( trim_start = = umax )
{
return { } ;
}
2023-04-18 22:59:21 +02:00
// Keep at least one preceding 0 (e.g. '00.3100' becomes '0.31' instead of '.31')
if ( version [ trim_start ] = = ' . ' )
{
if ( trim_start = = 0 )
{
// Version starts with '.' for some reason
return { } ;
}
trim_start - - ;
}
2023-04-18 20:07:02 +02:00
const usz dot_pos = version . find_first_of ( ' . ' , trim_start ) ;
if ( dot_pos = = umax )
{
return { } ;
}
// Try to keep the second 0 in the minor version (e.g. '04.9000' becomes '4.90' instead of '4.9')
const usz trim_end = std : : max ( version . find_last_not_of ( ' 0 ' , dot_pos + 1 ) , std : : min ( dot_pos + 2 , version . size ( ) ) ) ;
2021-03-05 15:18:17 +01:00
return std : : string ( version . substr ( trim_start , trim_end ) ) ;
2019-02-16 14:05:13 +01:00
}
2021-03-05 15:18:17 +01:00
return { } ;
2019-02-16 14:05:13 +01:00
}
2019-04-19 14:05:47 +02:00
std : : string utils : : get_OS_version ( )
{
2020-02-26 21:13:54 +01:00
std : : string output ;
2019-04-19 14:05:47 +02:00
# ifdef _WIN32
// GetVersionEx is deprecated, RtlGetVersion is kernel-mode only and AnalyticsInfo is UWP only.
// So we're forced to read PEB instead to get Windows version info. It's ugly but works.
2024-09-03 00:57:23 +02:00
# if defined(ARCH_X64)
2024-09-08 02:46:12 +02:00
const DWORD peb_offset = 0x60 ;
2019-04-19 14:05:47 +02:00
const INT_PTR peb = __readgsqword ( peb_offset ) ;
const DWORD version_major = * reinterpret_cast < const DWORD * > ( peb + 0x118 ) ;
const DWORD version_minor = * reinterpret_cast < const DWORD * > ( peb + 0x11c ) ;
const WORD build = * reinterpret_cast < const WORD * > ( peb + 0x120 ) ;
const UNICODE_STRING service_pack = * reinterpret_cast < const UNICODE_STRING * > ( peb + 0x02E8 ) ;
const u64 compatibility_mode = * reinterpret_cast < const u64 * > ( peb + 0x02C8 ) ; // Two DWORDs, major & minor version
const bool has_sp = service_pack . Length > 0 ;
std : : vector < char > holder ( service_pack . Length + 1 , ' \0 ' ) ;
if ( has_sp )
{
2023-07-11 20:40:30 +02:00
WideCharToMultiByte ( CP_UTF8 , 0 , service_pack . Buffer , service_pack . Length ,
static_cast < LPSTR > ( holder . data ( ) ) , static_cast < int > ( holder . size ( ) ) , nullptr , nullptr ) ;
2019-04-19 14:05:47 +02:00
}
fmt : : append ( output ,
" Operating system: Windows, Major: %lu, Minor: %lu, Build: %u, Service Pack: %s, Compatibility mode: %llu " ,
version_major , version_minor , build , has_sp ? holder . data ( ) : " none " , compatibility_mode ) ;
2024-09-08 02:46:12 +02:00
# else
// PEB cannot be easily accessed on ARM64, fall back to registry
static const auto s_windows_version = utils : : get_fallback_windows_version ( ) ;
return s_windows_version ;
# endif
2022-01-11 23:17:26 +01:00
# elif defined (__APPLE__)
const int major_version = Darwin_Version : : getNSmajorVersion ( ) ;
const int minor_version = Darwin_Version : : getNSminorVersion ( ) ;
const int patch_version = Darwin_Version : : getNSpatchVersion ( ) ;
fmt : : append ( output , " Operating system: macOS, Version: %d.%d.%d " ,
major_version , minor_version , patch_version ) ;
2019-04-19 14:05:47 +02:00
# else
struct utsname details = { } ;
if ( ! uname ( & details ) )
{
fmt : : append ( output , " Operating system: POSIX, Name: %s, Release: %s, Version: %s " ,
details . sysname , details . release , details . version ) ;
}
else
{
fmt : : append ( output , " Operating system: POSIX, Unknown version! (Error: %d) " , errno ) ;
}
# endif
return output ;
}
2019-07-15 16:20:12 +02:00
2022-01-10 14:15:04 +01:00
int utils : : get_maxfiles ( )
{
# ifdef _WIN32
// Virtually unlimited on Windows
return INT_MAX ;
# else
struct rlimit limits ;
ensure ( getrlimit ( RLIMIT_NOFILE , & limits ) = = 0 ) ;
return limits . rlim_cur ;
# endif
}
2022-03-05 17:32:35 +01:00
bool utils : : get_low_power_mode ( )
{
# ifdef __APPLE__
return Darwin_ProcessInfo : : getLowPowerModeEnabled ( ) ;
# else
return false ;
# endif
}
2020-04-06 21:29:53 +02:00
static constexpr ullong round_tsc ( ullong val )
{
2020-12-18 15:43:34 +01:00
return utils : : rounded_div ( val , 1'000'000 ) * 1'000'000 ;
2020-04-06 21:29:53 +02:00
}
2019-07-15 16:20:12 +02:00
ullong utils : : get_tsc_freq ( )
{
2020-04-07 10:02:12 +02:00
static const ullong cal_tsc = [ ] ( ) - > ullong
2020-04-06 17:23:45 +02:00
{
2022-01-18 02:21:42 +01:00
# ifdef ARCH_ARM64
u64 r = 0 ;
__asm__ volatile ( " mrs %0, cntfrq_el0 " : " =r " ( r ) ) ;
return r ;
# endif
2020-04-06 17:23:45 +02:00
if ( ! has_invariant_tsc ( ) )
return 0 ;
2020-04-09 14:31:34 +02:00
2019-07-15 16:20:12 +02:00
# ifdef _WIN32
2020-04-06 17:23:45 +02:00
LARGE_INTEGER freq ;
if ( ! QueryPerformanceFrequency ( & freq ) )
return 0 ;
if ( freq . QuadPart < = 9'999'999 )
2020-04-06 21:29:53 +02:00
return round_tsc ( freq . QuadPart * 1024 ) ;
2020-04-06 17:23:45 +02:00
const ullong timer_freq = freq . QuadPart ;
2019-07-15 16:20:12 +02:00
# else
2020-04-06 17:23:45 +02:00
const ullong timer_freq = 1'000'000'000 ;
2019-07-15 16:20:12 +02:00
# endif
2020-04-06 17:23:45 +02:00
// Calibrate TSC
constexpr int samples = 40 ;
ullong rdtsc_data [ samples ] ;
ullong timer_data [ samples ] ;
2021-02-17 20:58:10 +01:00
[[maybe_unused]] ullong error_data [ samples ] ;
2020-04-28 20:52:37 +02:00
// Narrow thread affinity to a single core
const u64 old_aff = thread_ctrl : : get_thread_affinity_mask ( ) ;
thread_ctrl : : set_thread_affinity_mask ( old_aff & ( 0 - old_aff ) ) ;
# ifndef _WIN32
struct timespec ts0 ;
clock_gettime ( CLOCK_MONOTONIC , & ts0 ) ;
ullong sec_base = ts0 . tv_sec ;
# endif
2020-04-06 17:23:45 +02:00
for ( int i = 0 ; i < samples ; i + + )
{
# ifdef _WIN32
2020-04-28 20:52:37 +02:00
Sleep ( 1 ) ;
2021-12-30 17:39:18 +01:00
error_data [ i ] = ( utils : : lfence ( ) , utils : : get_tsc ( ) ) ;
2020-04-06 17:23:45 +02:00
LARGE_INTEGER ctr ;
QueryPerformanceCounter ( & ctr ) ;
2021-12-30 17:39:18 +01:00
rdtsc_data [ i ] = ( utils : : lfence ( ) , utils : : get_tsc ( ) ) ;
2020-04-06 17:23:45 +02:00
timer_data [ i ] = ctr . QuadPart ;
# else
2020-04-28 20:52:37 +02:00
usleep ( 200 ) ;
2021-12-30 17:39:18 +01:00
error_data [ i ] = ( utils : : lfence ( ) , utils : : get_tsc ( ) ) ;
2020-04-06 17:23:45 +02:00
struct timespec ts ;
clock_gettime ( CLOCK_MONOTONIC , & ts ) ;
2021-12-30 17:39:18 +01:00
rdtsc_data [ i ] = ( utils : : lfence ( ) , utils : : get_tsc ( ) ) ;
2020-04-06 17:23:45 +02:00
timer_data [ i ] = ts . tv_nsec + ( ts . tv_sec - sec_base ) * 1'000'000'000 ;
# endif
}
2020-04-28 20:52:37 +02:00
// Restore main thread affinity
thread_ctrl : : set_thread_affinity_mask ( old_aff ) ;
2020-04-06 17:23:45 +02:00
// Compute average TSC
ullong acc = 0 ;
for ( int i = 0 ; i < samples - 1 ; i + + )
{
2020-04-09 14:31:34 +02:00
acc + = ( rdtsc_data [ i + 1 ] - rdtsc_data [ i ] ) * timer_freq / ( timer_data [ i + 1 ] - timer_data [ i ] ) ;
2020-04-06 17:23:45 +02:00
}
// Rounding
2020-04-06 21:29:53 +02:00
return round_tsc ( acc / ( samples - 1 ) ) ;
2020-04-06 17:23:45 +02:00
} ( ) ;
return cal_tsc ;
2019-07-15 16:20:12 +02:00
}
2020-02-20 03:55:25 +01:00
u64 utils : : get_total_memory ( )
{
# ifdef _WIN32
: : MEMORYSTATUSEX memInfo ;
memInfo . dwLength = sizeof ( memInfo ) ;
: : GlobalMemoryStatusEx ( & memInfo ) ;
return memInfo . ullTotalPhys ;
# else
return : : sysconf ( _SC_PHYS_PAGES ) * : : sysconf ( _SC_PAGE_SIZE ) ;
# endif
}
u32 utils : : get_thread_count ( )
{
2021-01-28 19:33:50 +01:00
static const u32 g_count = [ ] ( )
{
2020-02-20 03:55:25 +01:00
# ifdef _WIN32
2021-01-28 19:33:50 +01:00
: : SYSTEM_INFO sysInfo ;
: : GetNativeSystemInfo ( & sysInfo ) ;
return sysInfo . dwNumberOfProcessors ;
2020-02-20 03:55:25 +01:00
# else
2021-01-28 19:33:50 +01:00
return : : sysconf ( _SC_NPROCESSORS_ONLN ) ;
2020-02-20 03:55:25 +01:00
# endif
2021-01-28 19:33:50 +01:00
} ( ) ;
return g_count ;
2020-02-20 03:55:25 +01:00
}
2020-12-09 19:29:48 +01:00
u32 utils : : get_cpu_family ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2020-12-09 19:29:48 +01:00
static const u32 g_value = [ ] ( )
{
const u32 reg_value = get_cpuid ( 0x00000001 , 0 ) [ 0 ] ; // Processor feature info
const u32 base_value = ( reg_value > > 8 ) & 0xF ;
if ( base_value = = 0xF ) [[likely]]
{
const u32 extended_value = ( reg_value > > 20 ) & 0xFF ;
return base_value + extended_value ;
}
else
{
return base_value ;
}
} ( ) ;
return g_value ;
2021-12-30 17:39:18 +01:00
# elif defined(ARCH_ARM64)
return 0 ;
# endif
2020-12-09 19:29:48 +01:00
}
u32 utils : : get_cpu_model ( )
{
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2020-12-09 19:29:48 +01:00
static const u32 g_value = [ ] ( )
{
const u32 reg_value = get_cpuid ( 0x00000001 , 0 ) [ 0 ] ; // Processor feature info
const u32 base_value = ( reg_value > > 4 ) & 0xF ;
if ( const auto base_family_id = ( reg_value > > 8 ) & 0xF ;
base_family_id = = 0x6 | | base_family_id = = 0xF ) [[likely]]
{
const u32 extended_value = ( reg_value > > 16 ) & 0xF ;
return base_value + ( extended_value < < 4 ) ;
}
else
{
return base_value ;
}
} ( ) ;
return g_value ;
2021-12-30 17:39:18 +01:00
# elif defined(ARCH_ARM64)
return 0 ;
# endif
2020-12-09 19:29:48 +01:00
}
2021-03-06 18:02:16 +01:00
namespace utils
{
2022-07-09 13:42:03 +02:00
u64 _get_main_tid ( )
2021-03-06 18:02:16 +01:00
{
2022-07-09 13:42:03 +02:00
return thread_ctrl : : get_tid ( ) ;
}
2021-03-06 18:02:16 +01:00
}