| #include <errno.h> | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <cpuinfo.h> | |
| #include <cpuinfo/internal-api.h> | |
| #include <cpuinfo/log.h> | |
| #include "windows-arm-init.h" | |
| struct cpuinfo_arm_isa cpuinfo_isa; | |
| static void set_cpuinfo_isa_fields(void); | |
| static struct woa_chip_info* get_system_info_from_registry(void); | |
| static struct woa_chip_info woa_chip_unknown = { | |
| L"Unknown", | |
| woa_chip_name_unknown, | |
| {{cpuinfo_vendor_unknown, cpuinfo_uarch_unknown, 0}}}; | |
| /* Please add new SoC/chip info here! */ | |
| static struct woa_chip_info woa_chips[woa_chip_name_last] = { | |
| /* Microsoft SQ1 Kryo 495 4 + 4 cores (3 GHz + 1.80 GHz) */ | |
| [woa_chip_name_microsoft_sq_1] = | |
| {L"Microsoft SQ1", | |
| woa_chip_name_microsoft_sq_1, | |
| {{ | |
| cpuinfo_vendor_arm, | |
| cpuinfo_uarch_cortex_a55, | |
| 1800000000, | |
| }, | |
| { | |
| cpuinfo_vendor_arm, | |
| cpuinfo_uarch_cortex_a76, | |
| 3000000000, | |
| }}}, | |
| /* Microsoft SQ2 Kryo 495 4 + 4 cores (3.15 GHz + 2.42 GHz) */ | |
| [woa_chip_name_microsoft_sq_2] = | |
| {L"Microsoft SQ2", | |
| woa_chip_name_microsoft_sq_2, | |
| {{ | |
| cpuinfo_vendor_arm, | |
| cpuinfo_uarch_cortex_a55, | |
| 2420000000, | |
| }, | |
| {cpuinfo_vendor_arm, cpuinfo_uarch_cortex_a76, 3150000000}}}, | |
| /* Snapdragon (TM) 8cx Gen 3 @ 3.0 GHz */ | |
| [woa_chip_name_microsoft_sq_3] = | |
| {L"Snapdragon (TM) 8cx Gen 3", | |
| woa_chip_name_microsoft_sq_3, | |
| {{ | |
| cpuinfo_vendor_arm, | |
| cpuinfo_uarch_cortex_a78, | |
| 2420000000, | |
| }, | |
| {cpuinfo_vendor_arm, cpuinfo_uarch_cortex_x1, 3000000000}}}, | |
| /* Microsoft Windows Dev Kit 2023 */ | |
| [woa_chip_name_microsoft_sq_3_devkit] = | |
| {L"Snapdragon Compute Platform", | |
| woa_chip_name_microsoft_sq_3_devkit, | |
| {{ | |
| cpuinfo_vendor_arm, | |
| cpuinfo_uarch_cortex_a78, | |
| 2420000000, | |
| }, | |
| {cpuinfo_vendor_arm, cpuinfo_uarch_cortex_x1, 3000000000}}}, | |
| /* Ampere Altra */ | |
| [woa_chip_name_ampere_altra] = { | |
| L"Ampere(R) Altra(R) Processor", | |
| woa_chip_name_ampere_altra, | |
| {{cpuinfo_vendor_arm, cpuinfo_uarch_neoverse_n1, 3000000000}}}}; | |
| BOOL CALLBACK cpuinfo_arm_windows_init(PINIT_ONCE init_once, PVOID parameter, PVOID* context) { | |
| struct woa_chip_info* chip_info = NULL; | |
| enum cpuinfo_vendor vendor = cpuinfo_vendor_unknown; | |
| set_cpuinfo_isa_fields(); | |
| chip_info = get_system_info_from_registry(); | |
| if (chip_info == NULL) { | |
| chip_info = &woa_chip_unknown; | |
| } | |
| cpuinfo_is_initialized = cpu_info_init_by_logical_sys_info(chip_info, chip_info->uarchs[0].vendor); | |
| return true; | |
| } | |
| bool get_core_uarch_for_efficiency( | |
| enum woa_chip_name chip, | |
| BYTE EfficiencyClass, | |
| enum cpuinfo_uarch* uarch, | |
| uint64_t* frequency) { | |
| /* For currently supported WoA chips, the Efficiency class selects | |
| * the pre-defined little and big core. | |
| * Any further supported SoC's logic should be implemented here. | |
| */ | |
| if (uarch && frequency && chip < woa_chip_name_last && EfficiencyClass < MAX_WOA_VALID_EFFICIENCY_CLASSES) { | |
| *uarch = woa_chips[chip].uarchs[EfficiencyClass].uarch; | |
| *frequency = woa_chips[chip].uarchs[EfficiencyClass].frequency; | |
| return true; | |
| } | |
| return false; | |
| } | |
| /* Static helper functions */ | |
| static wchar_t* read_registry(LPCWSTR subkey, LPCWSTR value) { | |
| DWORD key_type = 0; | |
| DWORD data_size = 0; | |
| const DWORD flags = RRF_RT_REG_SZ; /* Only read strings (REG_SZ) */ | |
| wchar_t* text_buffer = NULL; | |
| LSTATUS result = 0; | |
| HANDLE heap = GetProcessHeap(); | |
| result = RegGetValueW( | |
| HKEY_LOCAL_MACHINE, | |
| subkey, | |
| value, | |
| flags, | |
| &key_type, | |
| NULL, /* Request buffer size */ | |
| &data_size); | |
| if (result != 0 || data_size == 0) { | |
| cpuinfo_log_error("Registry entry size read error"); | |
| return NULL; | |
| } | |
| text_buffer = HeapAlloc(heap, HEAP_ZERO_MEMORY, data_size); | |
| if (text_buffer == NULL) { | |
| cpuinfo_log_error("Registry textbuffer allocation error"); | |
| return NULL; | |
| } | |
| result = RegGetValueW( | |
| HKEY_LOCAL_MACHINE, | |
| subkey, | |
| value, | |
| flags, | |
| NULL, | |
| text_buffer, /* Write string in this destination buffer */ | |
| &data_size); | |
| if (result != 0) { | |
| cpuinfo_log_error("Registry read error"); | |
| HeapFree(heap, 0, text_buffer); | |
| return NULL; | |
| } | |
| return text_buffer; | |
| } | |
| static struct woa_chip_info* get_system_info_from_registry(void) { | |
| wchar_t* text_buffer = NULL; | |
| LPCWSTR cpu0_subkey = L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"; | |
| LPCWSTR chip_name_value = L"ProcessorNameString"; | |
| struct woa_chip_info* chip_info = NULL; | |
| HANDLE heap = GetProcessHeap(); | |
| /* Read processor model name from registry and find in the hard-coded | |
| * list. */ | |
| text_buffer = read_registry(cpu0_subkey, chip_name_value); | |
| if (text_buffer == NULL) { | |
| cpuinfo_log_error("Registry read error"); | |
| return NULL; | |
| } | |
| for (uint32_t i = 0; i < (uint32_t)woa_chip_name_last; i++) { | |
| size_t compare_length = wcsnlen(woa_chips[i].chip_name_string, CPUINFO_PACKAGE_NAME_MAX); | |
| int compare_result = wcsncmp(text_buffer, woa_chips[i].chip_name_string, compare_length); | |
| if (compare_result == 0) { | |
| chip_info = woa_chips + i; | |
| break; | |
| } | |
| } | |
| if (chip_info == NULL) { | |
| /* No match was found, so print a warning and assign the unknown | |
| * case. */ | |
| cpuinfo_log_error( | |
| "Unknown chip model name '%ls'.\nPlease add new Windows on Arm SoC/chip support to arm/windows/init.c!", | |
| text_buffer); | |
| } else { | |
| cpuinfo_log_debug("detected chip model name: %s", chip_info->chip_name_string); | |
| } | |
| HeapFree(heap, 0, text_buffer); | |
| return chip_info; | |
| } | |
| static void set_cpuinfo_isa_fields(void) { | |
| cpuinfo_isa.atomics = IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE) != 0; | |
| const bool dotprod = IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) != 0; | |
| cpuinfo_isa.dot = dotprod; | |
| SYSTEM_INFO system_info; | |
| GetSystemInfo(&system_info); | |
| switch (system_info.wProcessorLevel) { | |
| case 0x803: // Kryo 385 Silver (Snapdragon 850) | |
| cpuinfo_isa.fp16arith = dotprod; | |
| cpuinfo_isa.rdm = dotprod; | |
| break; | |
| default: | |
| // Assume that Dot Product support implies FP16 | |
| // arithmetics and RDM support. ARM manuals don't | |
| // guarantee that, but it holds in practice. | |
| cpuinfo_isa.fp16arith = dotprod; | |
| cpuinfo_isa.rdm = dotprod; | |
| break; | |
| } | |
| /* Windows API reports all or nothing for cryptographic instructions. */ | |
| const bool crypto = IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE) != 0; | |
| cpuinfo_isa.aes = crypto; | |
| cpuinfo_isa.sha1 = crypto; | |
| cpuinfo_isa.sha2 = crypto; | |
| cpuinfo_isa.pmull = crypto; | |
| cpuinfo_isa.crc32 = IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE) != 0; | |
| } |