• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <stdio.h>
2 #include <stdint.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <alloca.h>
6 
7 #include <errno.h>
8 #include <sys/types.h>
9 #include <sys/sysctl.h>
10 #include <mach/machine.h>
11 
12 #include <cpuinfo.h>
13 #include <mach/api.h>
14 #include <cpuinfo/internal-api.h>
15 #include <cpuinfo/log.h>
16 
17 /* Polyfill recent CPUFAMILY_ARM_* values for older SDKs */
18 #ifndef CPUFAMILY_ARM_MONSOON_MISTRAL
19 	#define CPUFAMILY_ARM_MONSOON_MISTRAL   0xE81E7EF6
20 #endif
21 #ifndef CPUFAMILY_ARM_VORTEX_TEMPEST
22 	#define CPUFAMILY_ARM_VORTEX_TEMPEST    0x07D34B9F
23 #endif
24 #ifndef CPUFAMILY_ARM_LIGHTNING_THUNDER
25 	#define CPUFAMILY_ARM_LIGHTNING_THUNDER 0x462504D2
26 #endif
27 #ifndef CPUFAMILY_ARM_FIRESTORM_ICESTORM
28 	#define CPUFAMILY_ARM_FIRESTORM_ICESTORM 0x1B588BB3
29 #endif
30 
31 struct cpuinfo_arm_isa cpuinfo_isa = {
32 #if CPUINFO_ARCH_ARM
33 	.thumb = true,
34 	.thumb2 = true,
35 	.thumbee = false,
36 	.jazelle = false,
37 	.armv5e = true,
38 	.armv6 = true,
39 	.armv6k = true,
40 	.armv7 = true,
41 	.vfpv2 = false,
42 	.vfpv3 = true,
43 	.d32 = true,
44 	.wmmx = false,
45 	.wmmx2 = false,
46 	.neon = true,
47 #endif
48 #if CPUINFO_ARCH_ARM64
49 	.aes = true,
50 	.sha1 = true,
51 	.sha2 = true,
52 	.pmull = true,
53 	.crc32 = true,
54 #endif
55 };
56 
get_sys_info(int type_specifier,const char * name)57 static uint32_t get_sys_info(int type_specifier, const char* name) {
58 	size_t size = 0;
59 	uint32_t result = 0;
60 	int mib[2] = { CTL_HW, type_specifier };
61 	if (sysctl(mib, 2, NULL, &size, NULL, 0) != 0) {
62 		cpuinfo_log_info("sysctl(\"%s\") failed: %s", name, strerror(errno));
63 	} else if (size == sizeof(uint32_t)) {
64 		sysctl(mib, 2, &result, &size, NULL, 0);
65 		cpuinfo_log_debug("%s: %"PRIu32 ", size = %lu", name, result, size);
66 	} else {
67 		cpuinfo_log_info("sysctl does not support non-integer lookup for (\"%s\")", name);
68 	}
69 	return result;
70 }
71 
get_sys_info_by_name(const char * type_specifier)72 static uint32_t get_sys_info_by_name(const char* type_specifier) {
73 	size_t size = 0;
74 	uint32_t result = 0;
75 	if (sysctlbyname(type_specifier, NULL, &size, NULL, 0) != 0) {
76 		cpuinfo_log_info("sysctlbyname(\"%s\") failed: %s", type_specifier, strerror(errno));
77 	} else if (size == sizeof(uint32_t)) {
78 		sysctlbyname(type_specifier, &result, &size, NULL, 0);
79 		cpuinfo_log_debug("%s: %"PRIu32 ", size = %lu", type_specifier, result, size);
80 	} else {
81 		cpuinfo_log_info("sysctl does not support non-integer lookup for (\"%s\")", type_specifier);
82 	}
83 	return result;
84 }
85 
decode_uarch(uint32_t cpu_family,uint32_t cpu_subtype,uint32_t core_index,uint32_t core_count)86 static enum cpuinfo_uarch decode_uarch(uint32_t cpu_family, uint32_t cpu_subtype, uint32_t core_index, uint32_t core_count) {
87 	switch (cpu_family) {
88 		case CPUFAMILY_ARM_SWIFT:
89 			return cpuinfo_uarch_swift;
90 		case CPUFAMILY_ARM_CYCLONE:
91 			return cpuinfo_uarch_cyclone;
92 		case CPUFAMILY_ARM_TYPHOON:
93 			return cpuinfo_uarch_typhoon;
94 		case CPUFAMILY_ARM_TWISTER:
95 			return cpuinfo_uarch_twister;
96 		case CPUFAMILY_ARM_HURRICANE:
97 			return cpuinfo_uarch_hurricane;
98 		case CPUFAMILY_ARM_MONSOON_MISTRAL:
99 			/* 2x Monsoon + 4x Mistral cores */
100 			return core_index < 2 ? cpuinfo_uarch_monsoon : cpuinfo_uarch_mistral;
101 		case CPUFAMILY_ARM_VORTEX_TEMPEST:
102 			/* Hexa-core: 2x Vortex + 4x Tempest; Octa-core: 4x Cortex + 4x Tempest */
103 			return core_index + 4 < core_count ? cpuinfo_uarch_vortex : cpuinfo_uarch_tempest;
104 		case CPUFAMILY_ARM_LIGHTNING_THUNDER:
105 			/* Hexa-core: 2x Lightning + 4x Thunder; Octa-core (presumed): 4x Lightning + 4x Thunder */
106 			return core_index + 4 < core_count ? cpuinfo_uarch_lightning : cpuinfo_uarch_thunder;
107 		case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
108 			/* Hexa-core: 2x Firestorm + 4x Icestorm; Octa-core: 4x Firestorm + 4x Icestorm */
109 			return core_index + 4 < core_count ? cpuinfo_uarch_firestorm : cpuinfo_uarch_icestorm;
110 		default:
111 			/* Use hw.cpusubtype for detection */
112 			break;
113 	}
114 
115 	#if CPUINFO_ARCH_ARM
116 		switch (cpu_subtype) {
117 			case CPU_SUBTYPE_ARM_V7:
118 				return cpuinfo_uarch_cortex_a8;
119 			case CPU_SUBTYPE_ARM_V7F:
120 				return cpuinfo_uarch_cortex_a9;
121 			case CPU_SUBTYPE_ARM_V7K:
122 				return cpuinfo_uarch_cortex_a7;
123 			default:
124 				return cpuinfo_uarch_unknown;
125 		}
126 	#else
127 		return cpuinfo_uarch_unknown;
128 	#endif
129 }
130 
decode_package_name(char * package_name)131 static void decode_package_name(char* package_name) {
132 	size_t size;
133 	if (sysctlbyname("hw.machine", NULL, &size, NULL, 0) != 0) {
134 		cpuinfo_log_warning("sysctlbyname(\"hw.machine\") failed: %s", strerror(errno));
135 		return;
136 	}
137 
138 	char *machine_name = alloca(size);
139 	if (sysctlbyname("hw.machine", machine_name, &size, NULL, 0) != 0) {
140 		cpuinfo_log_warning("sysctlbyname(\"hw.machine\") failed: %s", strerror(errno));
141 		return;
142 	}
143 	cpuinfo_log_debug("hw.machine: %s", machine_name);
144 
145 	char name[10];
146 	uint32_t major = 0, minor = 0;
147 	if (sscanf(machine_name, "%9[^,0123456789]%"SCNu32",%"SCNu32, name, &major, &minor) != 3) {
148 		cpuinfo_log_warning("parsing \"hw.machine\" failed: %s", strerror(errno));
149 		return;
150 	}
151 
152 	uint32_t chip_model = 0;
153 	char suffix = '\0';
154 	if (strcmp(name, "iPhone") == 0) {
155 		/*
156 		 * iPhone 4 and up are supported:
157 		 *  - iPhone 4       [A4]:  iPhone3,1, iPhone3,2, iPhone3,3
158 		 *  - iPhone 4S      [A5]:  iPhone4,1
159 		 *  - iPhone 5       [A6]:  iPhone5,1, iPhone5,2
160 		 *  - iPhone 5c      [A6]:  iPhone5,3, iPhone5,4
161 		 *  - iPhone 5s      [A7]:  iPhone6,1, iPhone6,2
162 		 *  - iPhone 6       [A8]:  iPhone7,2
163 		 *  - iPhone 6 Plus  [A8]:  iPhone7,1
164 		 *  - iPhone 6s      [A9]:  iPhone8,1
165 		 *  - iPhone 6s Plus [A9]:  iPhone8,2
166 		 *  - iPhone SE      [A9]:  iPhone8,4
167 		 *  - iPhone 7       [A10]: iPhone9,1, iPhone9,3
168 		 *  - iPhone 7 Plus  [A10]: iPhone9,2, iPhone9,4
169 		 *  - iPhone 8       [A11]: iPhone10,1, iPhone10,4
170 		 *  - iPhone 8 Plus  [A11]: iPhone10,2, iPhone10,5
171 		 *  - iPhone X       [A11]: iPhone10,3, iPhone10,6
172 		 *  - iPhone XS      [A12]: iPhone11,2,
173 		 *  - iPhone XS Max  [A12]: iPhone11,4, iPhone11,6
174 		 *  - iPhone XR      [A12]: iPhone11,8
175 		 */
176 		chip_model = major + 1;
177 	} else if (strcmp(name, "iPad") == 0) {
178 		switch (major) {
179 			/* iPad 2 and up are supported */
180 			case 2:
181 				/*
182 				 * iPad 2    [A5]: iPad2,1, iPad2,2, iPad2,3, iPad2,4
183 				 * iPad mini [A5]: iPad2,5, iPad2,6, iPad2,7
184 				 */
185 				chip_model = major + 3;
186 				break;
187 			case 3:
188 				/*
189 				 * iPad 3rd Gen [A5X]: iPad3,1, iPad3,2, iPad3,3
190 				 * iPad 4th Gen [A6X]: iPad3,4, iPad3,5, iPad3,6
191 				 */
192 				chip_model = (minor <= 3) ? 5 : 6;
193 				suffix = 'X';
194 				break;
195 			case 4:
196 				/*
197 				 * iPad Air         [A7]: iPad4,1, iPad4,2, iPad4,3
198 				 * iPad mini Retina [A7]: iPad4,4, iPad4,5, iPad4,6
199 				 * iPad mini 3      [A7]: iPad4,7, iPad4,8, iPad4,9
200 				 */
201 				chip_model = major + 3;
202 				break;
203 			case 5:
204 				/*
205 				 * iPad mini 4 [A8]:  iPad5,1, iPad5,2
206 				 * iPad Air 2  [A8X]: iPad5,3, iPad5,4
207 				 */
208 				chip_model = major + 3;
209 				suffix = (minor <= 2) ? '\0' : 'X';
210 				break;
211 			case 6:
212 				/*
213 				 * iPad Pro 9.7" [A9X]: iPad6,3, iPad6,4
214 				 * iPad Pro      [A9X]: iPad6,7, iPad6,8
215 				 * iPad 5th Gen  [A9]:  iPad6,11, iPad6,12
216 				 */
217 				chip_model = major + 3;
218 				suffix = minor <= 8 ? 'X' : '\0';
219 				break;
220 			case 7:
221 				/*
222 				 * iPad Pro 12.9" [A10X]: iPad7,1, iPad7,2
223 				 * iPad Pro 10.5" [A10X]: iPad7,3, iPad7,4
224 				 * iPad 6th Gen   [A10]:  iPad7,5, iPad7,6
225 				 */
226 				chip_model = major + 3;
227 				suffix = minor <= 4 ? 'X' : '\0';
228 				break;
229 			default:
230 				cpuinfo_log_info("unknown iPad: %s", machine_name);
231 				break;
232 		}
233 	} else if (strcmp(name, "iPod") == 0) {
234 		switch (major) {
235 			case 5:
236 				chip_model = 5;
237 				break;
238 				/* iPod touch (5th Gen) [A5]: iPod5,1 */
239 			case 7:
240 				/* iPod touch (6th Gen, 2015) [A8]: iPod7,1 */
241 				chip_model = 8;
242 				break;
243 			default:
244 				cpuinfo_log_info("unknown iPod: %s", machine_name);
245 				break;
246 		}
247 	} else {
248 		cpuinfo_log_info("unknown device: %s", machine_name);
249 	}
250 	if (chip_model != 0) {
251 		snprintf(package_name, CPUINFO_PACKAGE_NAME_MAX, "Apple A%"PRIu32"%c", chip_model, suffix);
252 	}
253 }
254 
cpuinfo_arm_mach_init(void)255 void cpuinfo_arm_mach_init(void) {
256 	struct cpuinfo_processor* processors = NULL;
257 	struct cpuinfo_core* cores = NULL;
258 	struct cpuinfo_cluster* clusters = NULL;
259 	struct cpuinfo_package* packages = NULL;
260 	struct cpuinfo_uarch_info* uarchs = NULL;
261 	struct cpuinfo_cache* l1i = NULL;
262 	struct cpuinfo_cache* l1d = NULL;
263 	struct cpuinfo_cache* l2 = NULL;
264 	struct cpuinfo_cache* l3 = NULL;
265 
266 	struct cpuinfo_mach_topology mach_topology = cpuinfo_mach_detect_topology();
267 	processors = calloc(mach_topology.threads, sizeof(struct cpuinfo_processor));
268 	if (processors == NULL) {
269 		cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" logical processors",
270 			mach_topology.threads * sizeof(struct cpuinfo_processor), mach_topology.threads);
271 		goto cleanup;
272 	}
273 	cores = calloc(mach_topology.cores, sizeof(struct cpuinfo_core));
274 	if (cores == NULL) {
275 		cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" cores",
276 			mach_topology.cores * sizeof(struct cpuinfo_core), mach_topology.cores);
277 		goto cleanup;
278 	}
279 	packages = calloc(mach_topology.packages, sizeof(struct cpuinfo_package));
280 	if (packages == NULL) {
281 		cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" packages",
282 			mach_topology.packages * sizeof(struct cpuinfo_package), mach_topology.packages);
283 		goto cleanup;
284 	}
285 
286 	const uint32_t threads_per_core = mach_topology.threads / mach_topology.cores;
287 	const uint32_t threads_per_package = mach_topology.threads / mach_topology.packages;
288 	const uint32_t cores_per_package = mach_topology.cores / mach_topology.packages;
289 
290 	for (uint32_t i = 0; i < mach_topology.packages; i++) {
291 		packages[i] = (struct cpuinfo_package) {
292 			.processor_start = i * threads_per_package,
293 			.processor_count = threads_per_package,
294 			.core_start = i * cores_per_package,
295 			.core_count = cores_per_package,
296 		};
297 		decode_package_name(packages[i].name);
298 	}
299 
300 
301 	const uint32_t cpu_family = get_sys_info_by_name("hw.cpufamily");
302 	const uint32_t cpu_type = get_sys_info_by_name("hw.cputype");
303 	const uint32_t cpu_subtype = get_sys_info_by_name("hw.cpusubtype");
304 	switch (cpu_type) {
305 		case CPU_TYPE_ARM64:
306 			cpuinfo_isa.aes = true;
307 			cpuinfo_isa.sha1 = true;
308 			cpuinfo_isa.sha2 = true;
309 			cpuinfo_isa.pmull = true;
310 			cpuinfo_isa.crc32 = true;
311 			break;
312 #if CPUINFO_ARCH_ARM
313 		case CPU_TYPE_ARM:
314 			switch (cpu_subtype) {
315 				case CPU_SUBTYPE_ARM_V8:
316 					cpuinfo_isa.armv8 = true;
317 					cpuinfo_isa.aes = true;
318 					cpuinfo_isa.sha1 = true;
319 					cpuinfo_isa.sha2 = true;
320 					cpuinfo_isa.pmull = true;
321 					cpuinfo_isa.crc32 = true;
322 					/* Fall-through to add ARMv7S features */
323 				case CPU_SUBTYPE_ARM_V7S:
324 				case CPU_SUBTYPE_ARM_V7K:
325 					cpuinfo_isa.fma = true;
326 					/* Fall-through to add ARMv7F features */
327 				case CPU_SUBTYPE_ARM_V7F:
328 					cpuinfo_isa.armv7mp = true;
329 					cpuinfo_isa.fp16 = true;
330 					/* Fall-through to add ARMv7 features */
331 				case CPU_SUBTYPE_ARM_V7:
332 					break;
333 				default:
334 					break;
335 			}
336 			break;
337 #endif
338 	}
339 	/*
340 	 * Support for ARMv8.1 Atomics & FP16 arithmetic instructions is supposed to be detected via
341 	 * sysctlbyname calls with "hw.optional.armv8_1_atomics" and "hw.optional.neon_fp16" arguments
342 	 * (see https://devstreaming-cdn.apple.com/videos/wwdc/2018/409t8zw7rumablsh/409/409_whats_new_in_llvm.pdf),
343 	 * but on new iOS versions these calls just fail with EPERM.
344 	 *
345 	 * Thus, we whitelist CPUs known to support these instructions.
346 	 */
347 	switch (cpu_family) {
348 		case CPUFAMILY_ARM_MONSOON_MISTRAL:
349 		case CPUFAMILY_ARM_VORTEX_TEMPEST:
350 		case CPUFAMILY_ARM_LIGHTNING_THUNDER:
351 		case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
352 			#if CPUINFO_ARCH_ARM64
353 				cpuinfo_isa.atomics = true;
354 			#endif
355 			cpuinfo_isa.fp16arith = true;
356 	}
357 
358 	/*
359 	 * There does not yet seem to exist an OS mechanism to detect support for
360 	 * ARMv8.2 optional dot-product instructions, so we currently whitelist CPUs
361 	 * known to support these instruction.
362 	 */
363 	switch (cpu_family) {
364 		case CPUFAMILY_ARM_LIGHTNING_THUNDER:
365 		case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
366 			cpuinfo_isa.dot = true;
367 	}
368 
369 	uint32_t num_clusters = 1;
370 	for (uint32_t i = 0; i < mach_topology.cores; i++) {
371 		cores[i] = (struct cpuinfo_core) {
372 			.processor_start = i * threads_per_core,
373 			.processor_count = threads_per_core,
374 			.core_id = i % cores_per_package,
375 			.package = packages + i / cores_per_package,
376 			.vendor = cpuinfo_vendor_apple,
377 			.uarch = decode_uarch(cpu_family, cpu_subtype, i, mach_topology.cores),
378 		};
379 		if (i != 0 && cores[i].uarch != cores[i - 1].uarch) {
380 			num_clusters++;
381 		}
382 	}
383 	for (uint32_t i = 0; i < mach_topology.threads; i++) {
384 		const uint32_t smt_id = i % threads_per_core;
385 		const uint32_t core_id = i / threads_per_core;
386 		const uint32_t package_id = i / threads_per_package;
387 
388 		processors[i].smt_id = smt_id;
389 		processors[i].core = &cores[core_id];
390 		processors[i].package = &packages[package_id];
391 	}
392 
393 	clusters = calloc(num_clusters, sizeof(struct cpuinfo_cluster));
394 	if (clusters == NULL) {
395 		cpuinfo_log_error(
396 			"failed to allocate %zu bytes for descriptions of %"PRIu32" clusters",
397 			num_clusters * sizeof(struct cpuinfo_cluster), num_clusters);
398 		goto cleanup;
399 	}
400 	uarchs = calloc(num_clusters, sizeof(struct cpuinfo_uarch_info));
401 	if (uarchs == NULL) {
402 		cpuinfo_log_error(
403 			"failed to allocate %zu bytes for descriptions of %"PRIu32" uarchs",
404 			num_clusters * sizeof(enum cpuinfo_uarch), num_clusters);
405 		goto cleanup;
406 	}
407 	uint32_t cluster_idx = UINT32_MAX;
408 	for (uint32_t i = 0; i < mach_topology.cores; i++) {
409 		if (i == 0 || cores[i].uarch != cores[i - 1].uarch) {
410 			cluster_idx++;
411 			uarchs[cluster_idx] = (struct cpuinfo_uarch_info) {
412 				.uarch = cores[i].uarch,
413 				.processor_count = 1,
414 				.core_count = 1,
415 			};
416 			clusters[cluster_idx] = (struct cpuinfo_cluster) {
417 				.processor_start = i * threads_per_core,
418 				.processor_count = 1,
419 				.core_start = i,
420 				.core_count = 1,
421 				.cluster_id = cluster_idx,
422 				.package = cores[i].package,
423 				.vendor = cores[i].vendor,
424 				.uarch = cores[i].uarch,
425 			};
426 		} else {
427 			uarchs[cluster_idx].processor_count++;
428 			uarchs[cluster_idx].core_count++;
429 			clusters[cluster_idx].processor_count++;
430 			clusters[cluster_idx].core_count++;
431 		}
432 		cores[i].cluster = &clusters[cluster_idx];
433 	}
434 
435 	for (uint32_t i = 0; i < mach_topology.threads; i++) {
436 		const uint32_t core_id = i / threads_per_core;
437 		processors[i].cluster = cores[core_id].cluster;
438 	}
439 
440 	for (uint32_t i = 0; i < mach_topology.packages; i++) {
441 		packages[i].cluster_start = 0;
442 		packages[i].cluster_count = num_clusters;
443 	}
444 
445 	const uint32_t cacheline_size = get_sys_info(HW_CACHELINE, "HW_CACHELINE");
446 	const uint32_t l1d_cache_size = get_sys_info(HW_L1DCACHESIZE, "HW_L1DCACHESIZE");
447 	const uint32_t l1i_cache_size = get_sys_info(HW_L1ICACHESIZE, "HW_L1ICACHESIZE");
448 	const uint32_t l2_cache_size = get_sys_info(HW_L2CACHESIZE, "HW_L2CACHESIZE");
449 	const uint32_t l3_cache_size = get_sys_info(HW_L3CACHESIZE, "HW_L3CACHESIZE");
450 	const uint32_t l1_cache_associativity = 4;
451 	const uint32_t l2_cache_associativity = 8;
452 	const uint32_t l3_cache_associativity = 16;
453 	const uint32_t cache_partitions = 1;
454 	const uint32_t cache_flags = 0;
455 
456 	uint32_t threads_per_l1 = 0, l1_count = 0;
457 	if (l1i_cache_size != 0 || l1d_cache_size != 0) {
458 		/* Assume L1 caches are private to each core */
459 		threads_per_l1 = 1;
460 		l1_count = mach_topology.threads / threads_per_l1;
461 		cpuinfo_log_debug("detected %"PRIu32" L1 caches", l1_count);
462 	}
463 
464 	uint32_t threads_per_l2 = 0, l2_count = 0;
465 	if (l2_cache_size != 0) {
466 		/* Assume L2 cache is shared between all cores */
467 		threads_per_l2 = mach_topology.cores;
468 		l2_count = 1;
469 		cpuinfo_log_debug("detected %"PRIu32" L2 caches", l2_count);
470 	}
471 
472 	uint32_t threads_per_l3 = 0, l3_count = 0;
473 	if (l3_cache_size != 0) {
474 		/* Assume L3 cache is shared between all cores */
475 		threads_per_l3 = mach_topology.cores;
476 		l3_count = 1;
477 		cpuinfo_log_debug("detected %"PRIu32" L3 caches", l3_count);
478 	}
479 
480 	if (l1i_cache_size != 0) {
481 		l1i = calloc(l1_count, sizeof(struct cpuinfo_cache));
482 		if (l1i == NULL) {
483 			cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L1I caches",
484 				l1_count * sizeof(struct cpuinfo_cache), l1_count);
485 			goto cleanup;
486 		}
487 		for (uint32_t c = 0; c < l1_count; c++) {
488 			l1i[c] = (struct cpuinfo_cache) {
489 				.size            = l1i_cache_size,
490 				.associativity   = l1_cache_associativity,
491 				.sets            = l1i_cache_size / (l1_cache_associativity * cacheline_size),
492 				.partitions      = cache_partitions,
493 				.line_size       = cacheline_size,
494 				.flags           = cache_flags,
495 				.processor_start = c * threads_per_l1,
496 				.processor_count = threads_per_l1,
497 			};
498 		}
499 		for (uint32_t t = 0; t < mach_topology.threads; t++) {
500 			processors[t].cache.l1i = &l1i[t / threads_per_l1];
501 		}
502 	}
503 
504 	if (l1d_cache_size != 0) {
505 		l1d = calloc(l1_count, sizeof(struct cpuinfo_cache));
506 		if (l1d == NULL) {
507 			cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L1D caches",
508 				l1_count * sizeof(struct cpuinfo_cache), l1_count);
509 			goto cleanup;
510 		}
511 		for (uint32_t c = 0; c < l1_count; c++) {
512 			l1d[c] = (struct cpuinfo_cache) {
513 				.size            = l1d_cache_size,
514 				.associativity   = l1_cache_associativity,
515 				.sets            = l1d_cache_size / (l1_cache_associativity * cacheline_size),
516 				.partitions      = cache_partitions,
517 				.line_size       = cacheline_size,
518 				.flags           = cache_flags,
519 				.processor_start = c * threads_per_l1,
520 				.processor_count = threads_per_l1,
521 			};
522 		}
523 		for (uint32_t t = 0; t < mach_topology.threads; t++) {
524 			processors[t].cache.l1d = &l1d[t / threads_per_l1];
525 		}
526 	}
527 
528 	if (l2_count != 0) {
529 		l2 = calloc(l2_count, sizeof(struct cpuinfo_cache));
530 		if (l2 == NULL) {
531 			cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L2 caches",
532 				l2_count * sizeof(struct cpuinfo_cache), l2_count);
533 			goto cleanup;
534 		}
535 		for (uint32_t c = 0; c < l2_count; c++) {
536 			l2[c] = (struct cpuinfo_cache) {
537 				.size            = l2_cache_size,
538 				.associativity   = l2_cache_associativity,
539 				.sets            = l2_cache_size / (l2_cache_associativity * cacheline_size),
540 				.partitions      = cache_partitions,
541 				.line_size       = cacheline_size,
542 				.flags           = cache_flags,
543 				.processor_start = c * threads_per_l2,
544 				.processor_count = threads_per_l2,
545 			};
546 		}
547 		for (uint32_t t = 0; t < mach_topology.threads; t++) {
548 			processors[t].cache.l2 = &l2[0];
549 		}
550 	}
551 
552 	if (l3_count != 0) {
553 		l3 = calloc(l3_count, sizeof(struct cpuinfo_cache));
554 		if (l3 == NULL) {
555 			cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L3 caches",
556 												l3_count * sizeof(struct cpuinfo_cache), l3_count);
557 			goto cleanup;
558 		}
559 		for (uint32_t c = 0; c < l3_count; c++) {
560 			l3[c] = (struct cpuinfo_cache) {
561 				.size            = l3_cache_size,
562 				.associativity   = l3_cache_associativity,
563 				.sets            = l3_cache_size / (l3_cache_associativity * cacheline_size),
564 				.partitions      = cache_partitions,
565 				.line_size       = cacheline_size,
566 				.flags           = cache_flags,
567 				.processor_start = c * threads_per_l3,
568 				.processor_count = threads_per_l3,
569 			};
570 		}
571 		for (uint32_t t = 0; t < mach_topology.threads; t++) {
572 			processors[t].cache.l3 = &l3[0];
573 		}
574 	}
575 
576 	/* Commit changes */
577 	cpuinfo_processors = processors;
578 	cpuinfo_cores = cores;
579 	cpuinfo_clusters = clusters;
580 	cpuinfo_packages = packages;
581 	cpuinfo_uarchs = uarchs;
582 	cpuinfo_cache[cpuinfo_cache_level_1i] = l1i;
583 	cpuinfo_cache[cpuinfo_cache_level_1d] = l1d;
584 	cpuinfo_cache[cpuinfo_cache_level_2]  = l2;
585 	cpuinfo_cache[cpuinfo_cache_level_3]  = l3;
586 
587 	cpuinfo_processors_count = mach_topology.threads;
588 	cpuinfo_cores_count = mach_topology.cores;
589 	cpuinfo_clusters_count = num_clusters;
590 	cpuinfo_packages_count = mach_topology.packages;
591 	cpuinfo_uarchs_count = num_clusters;
592 	cpuinfo_cache_count[cpuinfo_cache_level_1i] = l1_count;
593 	cpuinfo_cache_count[cpuinfo_cache_level_1d] = l1_count;
594 	cpuinfo_cache_count[cpuinfo_cache_level_2]  = l2_count;
595 	cpuinfo_cache_count[cpuinfo_cache_level_3]  = l3_count;
596 	cpuinfo_max_cache_size = cpuinfo_compute_max_cache_size(&processors[0]);
597 
598 	__sync_synchronize();
599 
600 	cpuinfo_is_initialized = true;
601 
602 	processors = NULL;
603 	cores = NULL;
604 	clusters = NULL;
605 	packages = NULL;
606 	uarchs = NULL;
607 	l1i = l1d = l2 = l3 = NULL;
608 
609 cleanup:
610 	free(processors);
611 	free(cores);
612 	free(clusters);
613 	free(packages);
614 	free(uarchs);
615 	free(l1i);
616 	free(l1d);
617 	free(l2);
618 	free(l3);
619 }
620