1 #include <errno.h>
2 #include <stdint.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5
6 #include <cpuinfo.h>
7 #include <cpuinfo/internal-api.h>
8 #include <cpuinfo/log.h>
9
10 #include "windows-arm-init.h"
11
12 struct cpuinfo_arm_isa cpuinfo_isa;
13
14 static void set_cpuinfo_isa_fields(void);
15 static struct woa_chip_info* get_system_info_from_registry(void);
16
17 static struct woa_chip_info woa_chip_unknown = {
18 L"Unknown",
19 woa_chip_name_unknown,
20 {{cpuinfo_vendor_unknown, cpuinfo_uarch_unknown, 0}}};
21
22 /* Please add new SoC/chip info here! */
23 static struct woa_chip_info woa_chips[woa_chip_name_last] = {
24 /* Microsoft SQ1 Kryo 495 4 + 4 cores (3 GHz + 1.80 GHz) */
25 [woa_chip_name_microsoft_sq_1] =
26 {L"Microsoft SQ1",
27 woa_chip_name_microsoft_sq_1,
28 {{
29 cpuinfo_vendor_arm,
30 cpuinfo_uarch_cortex_a55,
31 1800000000,
32 },
33 {
34 cpuinfo_vendor_arm,
35 cpuinfo_uarch_cortex_a76,
36 3000000000,
37 }}},
38 /* Microsoft SQ2 Kryo 495 4 + 4 cores (3.15 GHz + 2.42 GHz) */
39 [woa_chip_name_microsoft_sq_2] =
40 {L"Microsoft SQ2",
41 woa_chip_name_microsoft_sq_2,
42 {{
43 cpuinfo_vendor_arm,
44 cpuinfo_uarch_cortex_a55,
45 2420000000,
46 },
47 {cpuinfo_vendor_arm, cpuinfo_uarch_cortex_a76, 3150000000}}},
48 /* Snapdragon (TM) 8cx Gen 3 @ 3.0 GHz */
49 [woa_chip_name_microsoft_sq_3] =
50 {L"Snapdragon (TM) 8cx Gen 3",
51 woa_chip_name_microsoft_sq_3,
52 {{
53 cpuinfo_vendor_arm,
54 cpuinfo_uarch_cortex_a78,
55 2420000000,
56 },
57 {cpuinfo_vendor_arm, cpuinfo_uarch_cortex_x1, 3000000000}}},
58 /* Microsoft Windows Dev Kit 2023 */
59 [woa_chip_name_microsoft_sq_3_devkit] =
60 {L"Snapdragon Compute Platform",
61 woa_chip_name_microsoft_sq_3_devkit,
62 {{
63 cpuinfo_vendor_arm,
64 cpuinfo_uarch_cortex_a78,
65 2420000000,
66 },
67 {cpuinfo_vendor_arm, cpuinfo_uarch_cortex_x1, 3000000000}}},
68 /* Ampere Altra */
69 [woa_chip_name_ampere_altra] = {
70 L"Ampere(R) Altra(R) Processor",
71 woa_chip_name_ampere_altra,
72 {{cpuinfo_vendor_arm, cpuinfo_uarch_neoverse_n1, 3000000000}}}};
73
cpuinfo_arm_windows_init(PINIT_ONCE init_once,PVOID parameter,PVOID * context)74 BOOL CALLBACK cpuinfo_arm_windows_init(PINIT_ONCE init_once, PVOID parameter, PVOID* context) {
75 struct woa_chip_info* chip_info = NULL;
76 enum cpuinfo_vendor vendor = cpuinfo_vendor_unknown;
77
78 set_cpuinfo_isa_fields();
79
80 chip_info = get_system_info_from_registry();
81 if (chip_info == NULL) {
82 chip_info = &woa_chip_unknown;
83 }
84
85 cpuinfo_is_initialized = cpu_info_init_by_logical_sys_info(chip_info, chip_info->uarchs[0].vendor);
86
87 return true;
88 }
89
get_core_uarch_for_efficiency(enum woa_chip_name chip,BYTE EfficiencyClass,enum cpuinfo_uarch * uarch,uint64_t * frequency)90 bool get_core_uarch_for_efficiency(
91 enum woa_chip_name chip,
92 BYTE EfficiencyClass,
93 enum cpuinfo_uarch* uarch,
94 uint64_t* frequency) {
95 /* For currently supported WoA chips, the Efficiency class selects
96 * the pre-defined little and big core.
97 * Any further supported SoC's logic should be implemented here.
98 */
99 if (uarch && frequency && chip < woa_chip_name_last && EfficiencyClass < MAX_WOA_VALID_EFFICIENCY_CLASSES) {
100 *uarch = woa_chips[chip].uarchs[EfficiencyClass].uarch;
101 *frequency = woa_chips[chip].uarchs[EfficiencyClass].frequency;
102 return true;
103 }
104 return false;
105 }
106
107 /* Static helper functions */
108
read_registry(LPCWSTR subkey,LPCWSTR value)109 static wchar_t* read_registry(LPCWSTR subkey, LPCWSTR value) {
110 DWORD key_type = 0;
111 DWORD data_size = 0;
112 const DWORD flags = RRF_RT_REG_SZ; /* Only read strings (REG_SZ) */
113 wchar_t* text_buffer = NULL;
114 LSTATUS result = 0;
115 HANDLE heap = GetProcessHeap();
116
117 result = RegGetValueW(
118 HKEY_LOCAL_MACHINE,
119 subkey,
120 value,
121 flags,
122 &key_type,
123 NULL, /* Request buffer size */
124 &data_size);
125 if (result != 0 || data_size == 0) {
126 cpuinfo_log_error("Registry entry size read error");
127 return NULL;
128 }
129
130 text_buffer = HeapAlloc(heap, HEAP_ZERO_MEMORY, data_size);
131 if (text_buffer == NULL) {
132 cpuinfo_log_error("Registry textbuffer allocation error");
133 return NULL;
134 }
135
136 result = RegGetValueW(
137 HKEY_LOCAL_MACHINE,
138 subkey,
139 value,
140 flags,
141 NULL,
142 text_buffer, /* Write string in this destination buffer */
143 &data_size);
144 if (result != 0) {
145 cpuinfo_log_error("Registry read error");
146 HeapFree(heap, 0, text_buffer);
147 return NULL;
148 }
149 return text_buffer;
150 }
151
get_system_info_from_registry(void)152 static struct woa_chip_info* get_system_info_from_registry(void) {
153 wchar_t* text_buffer = NULL;
154 LPCWSTR cpu0_subkey = L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
155 LPCWSTR chip_name_value = L"ProcessorNameString";
156 struct woa_chip_info* chip_info = NULL;
157
158 HANDLE heap = GetProcessHeap();
159
160 /* Read processor model name from registry and find in the hard-coded
161 * list. */
162 text_buffer = read_registry(cpu0_subkey, chip_name_value);
163 if (text_buffer == NULL) {
164 cpuinfo_log_error("Registry read error");
165 return NULL;
166 }
167 for (uint32_t i = 0; i < (uint32_t)woa_chip_name_last; i++) {
168 size_t compare_length = wcsnlen(woa_chips[i].chip_name_string, CPUINFO_PACKAGE_NAME_MAX);
169 int compare_result = wcsncmp(text_buffer, woa_chips[i].chip_name_string, compare_length);
170 if (compare_result == 0) {
171 chip_info = woa_chips + i;
172 break;
173 }
174 }
175 if (chip_info == NULL) {
176 /* No match was found, so print a warning and assign the unknown
177 * case. */
178 cpuinfo_log_error(
179 "Unknown chip model name '%ls'.\nPlease add new Windows on Arm SoC/chip support to arm/windows/init.c!",
180 text_buffer);
181 } else {
182 cpuinfo_log_debug("detected chip model name: %s", chip_info->chip_name_string);
183 }
184
185 HeapFree(heap, 0, text_buffer);
186 return chip_info;
187 }
188
set_cpuinfo_isa_fields(void)189 static void set_cpuinfo_isa_fields(void) {
190 cpuinfo_isa.atomics = IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE) != 0;
191
192 const bool dotprod = IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) != 0;
193 cpuinfo_isa.dot = dotprod;
194
195 SYSTEM_INFO system_info;
196 GetSystemInfo(&system_info);
197 switch (system_info.wProcessorLevel) {
198 case 0x803: // Kryo 385 Silver (Snapdragon 850)
199 cpuinfo_isa.fp16arith = dotprod;
200 cpuinfo_isa.rdm = dotprod;
201 break;
202 default:
203 // Assume that Dot Product support implies FP16
204 // arithmetics and RDM support. ARM manuals don't
205 // guarantee that, but it holds in practice.
206 cpuinfo_isa.fp16arith = dotprod;
207 cpuinfo_isa.rdm = dotprod;
208 break;
209 }
210
211 /* Windows API reports all or nothing for cryptographic instructions. */
212 const bool crypto = IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE) != 0;
213 cpuinfo_isa.aes = crypto;
214 cpuinfo_isa.sha1 = crypto;
215 cpuinfo_isa.sha2 = crypto;
216 cpuinfo_isa.pmull = crypto;
217
218 cpuinfo_isa.crc32 = IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE) != 0;
219 }
220