xref: /aosp_15_r20/external/cpuinfo/src/arm/linux/cpuinfo.c (revision 2b54f0db79fd8303838913b20ff3780cddaa909f)
1*2b54f0dbSXin Li #include <stdbool.h>
2*2b54f0dbSXin Li #include <stdint.h>
3*2b54f0dbSXin Li #include <stdlib.h>
4*2b54f0dbSXin Li #include <stddef.h>
5*2b54f0dbSXin Li #include <string.h>
6*2b54f0dbSXin Li 
7*2b54f0dbSXin Li #include <linux/api.h>
8*2b54f0dbSXin Li #include <arm/linux/api.h>
9*2b54f0dbSXin Li #include <arm/midr.h>
10*2b54f0dbSXin Li #include <cpuinfo/log.h>
11*2b54f0dbSXin Li 
12*2b54f0dbSXin Li /*
13*2b54f0dbSXin Li  * Size, in chars, of the on-stack buffer used for parsing lines of /proc/cpuinfo.
14*2b54f0dbSXin Li  * This is also the limit on the length of a single line.
15*2b54f0dbSXin Li  */
16*2b54f0dbSXin Li #define BUFFER_SIZE 1024
17*2b54f0dbSXin Li 
18*2b54f0dbSXin Li 
parse_processor_number(const char * processor_start,const char * processor_end)19*2b54f0dbSXin Li static uint32_t parse_processor_number(
20*2b54f0dbSXin Li 	const char* processor_start,
21*2b54f0dbSXin Li 	const char* processor_end)
22*2b54f0dbSXin Li {
23*2b54f0dbSXin Li 	const size_t processor_length = (size_t) (processor_end - processor_start);
24*2b54f0dbSXin Li 
25*2b54f0dbSXin Li 	if (processor_length == 0) {
26*2b54f0dbSXin Li 		cpuinfo_log_warning("Processor number in /proc/cpuinfo is ignored: string is empty");
27*2b54f0dbSXin Li 		return 0;
28*2b54f0dbSXin Li 	}
29*2b54f0dbSXin Li 
30*2b54f0dbSXin Li 	uint32_t processor_number = 0;
31*2b54f0dbSXin Li 	for (const char* digit_ptr = processor_start; digit_ptr != processor_end; digit_ptr++) {
32*2b54f0dbSXin Li 		const uint32_t digit = (uint32_t) (*digit_ptr - '0');
33*2b54f0dbSXin Li 		if (digit > 10) {
34*2b54f0dbSXin Li 			cpuinfo_log_warning("non-decimal suffix %.*s in /proc/cpuinfo processor number is ignored",
35*2b54f0dbSXin Li 				(int) (processor_end - digit_ptr), digit_ptr);
36*2b54f0dbSXin Li 			break;
37*2b54f0dbSXin Li 		}
38*2b54f0dbSXin Li 
39*2b54f0dbSXin Li 		processor_number = processor_number * 10 + digit;
40*2b54f0dbSXin Li 	}
41*2b54f0dbSXin Li 
42*2b54f0dbSXin Li 	return processor_number;
43*2b54f0dbSXin Li }
44*2b54f0dbSXin Li 
45*2b54f0dbSXin Li /*
46*2b54f0dbSXin Li  *	Full list of ARM features reported in /proc/cpuinfo:
47*2b54f0dbSXin Li  *
48*2b54f0dbSXin Li  *	* swp - support for SWP instruction (deprecated in ARMv7, can be removed in future)
49*2b54f0dbSXin Li  *	* half - support for half-word loads and stores. These instruction are part of ARMv4,
50*2b54f0dbSXin Li  *	         so no need to check it on supported CPUs.
51*2b54f0dbSXin Li  *	* thumb - support for 16-bit Thumb instruction set. Note that BX instruction is detected
52*2b54f0dbSXin Li  *	          by ARMv4T architecture, not by this flag.
53*2b54f0dbSXin Li  *	* 26bit - old CPUs merged 26-bit PC and program status register (flags) into 32-bit PC
54*2b54f0dbSXin Li  *	          and had special instructions for working with packed PC. Now it is all deprecated.
55*2b54f0dbSXin Li  *	* fastmult - most old ARM CPUs could only compute 2 bits of multiplication result per clock
56*2b54f0dbSXin Li  *	             cycle, but CPUs with M suffix (e.g. ARM7TDMI) could compute 4 bits per cycle.
57*2b54f0dbSXin Li  *	             Of course, now it makes no sense.
58*2b54f0dbSXin Li  *	* fpa - floating point accelerator available. On original ARM ABI all floating-point operations
59*2b54f0dbSXin Li  *	        generated FPA instructions. If FPA was not available, these instructions generated
60*2b54f0dbSXin Li  *	        "illegal operation" interrupts, and the OS processed them by emulating the FPA instructions.
61*2b54f0dbSXin Li  *	        Debian used this ABI before it switched to EABI. Now FPA is deprecated.
62*2b54f0dbSXin Li  *	* vfp - vector floating point instructions. Available on most modern CPUs (as part of VFPv3).
63*2b54f0dbSXin Li  *	        Required by Android ARMv7A ABI and by Ubuntu on ARM.
64*2b54f0dbSXin Li  *              Note: there is no flag for VFPv2.
65*2b54f0dbSXin Li  *	* edsp - V5E instructions: saturating add/sub and 16-bit x 16-bit -> 32/64-bit multiplications.
66*2b54f0dbSXin Li  *	         Required on Android, supported by all CPUs in production.
67*2b54f0dbSXin Li  *	* java - Jazelle extension. Supported on most CPUs.
68*2b54f0dbSXin Li  *	* iwmmxt - Intel/Marvell Wireless MMX instructions. 64-bit integer SIMD.
69*2b54f0dbSXin Li  *	           Supported on XScale (Since PXA270) and Sheeva (PJ1, PJ4) architectures.
70*2b54f0dbSXin Li  *	           Note that there is no flag for WMMX2 instructions.
71*2b54f0dbSXin Li  *	* crunch - Maverick Crunch instructions. Junk.
72*2b54f0dbSXin Li  *	* thumbee - ThumbEE instructions. Almost no documentation is available.
73*2b54f0dbSXin Li  *	* neon - NEON instructions (aka Advanced SIMD). MVFR1 register gives more
74*2b54f0dbSXin Li  *	         fine-grained information on particular supported features, but
75*2b54f0dbSXin Li  *	         the Linux kernel exports only a single flag for all of them.
76*2b54f0dbSXin Li  *	         According to ARMv7A docs it also implies the availability of VFPv3
77*2b54f0dbSXin Li  *	         (with 32 double-precision registers d0-d31).
78*2b54f0dbSXin Li  *	* vfpv3 - VFPv3 instructions. Available on most modern CPUs. Augment VFPv2 by
79*2b54f0dbSXin Li  *	          conversion to/from integers and load constant instructions.
80*2b54f0dbSXin Li  *	          Required by Android ARMv7A ABI and by Ubuntu on ARM.
81*2b54f0dbSXin Li  *	* vfpv3d16 - VFPv3 instructions with only 16 double-precision registers (d0-d15).
82*2b54f0dbSXin Li  *	* tls - software thread ID registers.
83*2b54f0dbSXin Li  *	        Used by kernel (and likely libc) for efficient implementation of TLS.
84*2b54f0dbSXin Li  *	* vfpv4 - fused multiply-add instructions.
85*2b54f0dbSXin Li  *	* idiva - DIV instructions available in ARM mode.
86*2b54f0dbSXin Li  *	* idivt - DIV instructions available in Thumb mode.
87*2b54f0dbSXin Li  *  * vfpd32 - VFP (of any version) with 32 double-precision registers d0-d31.
88*2b54f0dbSXin Li  *  * lpae - Large Physical Address Extension (physical address up to 40 bits).
89*2b54f0dbSXin Li  *  * evtstrm - generation of Event Stream by timer.
90*2b54f0dbSXin Li  *  * aes - AES instructions.
91*2b54f0dbSXin Li  *  * pmull - Polinomial Multiplication instructions.
92*2b54f0dbSXin Li  *  * sha1 - SHA1 instructions.
93*2b54f0dbSXin Li  *  * sha2 - SHA2 instructions.
94*2b54f0dbSXin Li  *  * crc32 - CRC32 instructions.
95*2b54f0dbSXin Li  *
96*2b54f0dbSXin Li  *	/proc/cpuinfo on ARM is populated in file arch/arm/kernel/setup.c in Linux kernel
97*2b54f0dbSXin Li  *	Note that some devices may use patched Linux kernels with different feature names.
98*2b54f0dbSXin Li  *	However, the names above were checked on a large number of /proc/cpuinfo listings.
99*2b54f0dbSXin Li  */
parse_features(const char * features_start,const char * features_end,struct cpuinfo_arm_linux_processor processor[restrict static1])100*2b54f0dbSXin Li static void parse_features(
101*2b54f0dbSXin Li 	const char* features_start,
102*2b54f0dbSXin Li 	const char* features_end,
103*2b54f0dbSXin Li 	struct cpuinfo_arm_linux_processor processor[restrict static 1])
104*2b54f0dbSXin Li {
105*2b54f0dbSXin Li 	const char* feature_start = features_start;
106*2b54f0dbSXin Li 	const char* feature_end;
107*2b54f0dbSXin Li 
108*2b54f0dbSXin Li 	/* Mark the features as valid */
109*2b54f0dbSXin Li 	processor->flags |= CPUINFO_ARM_LINUX_VALID_FEATURES | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
110*2b54f0dbSXin Li 
111*2b54f0dbSXin Li 	do {
112*2b54f0dbSXin Li 		feature_end = feature_start + 1;
113*2b54f0dbSXin Li 		for (; feature_end != features_end; feature_end++) {
114*2b54f0dbSXin Li 			if (*feature_end == ' ') {
115*2b54f0dbSXin Li 				break;
116*2b54f0dbSXin Li 			}
117*2b54f0dbSXin Li 		}
118*2b54f0dbSXin Li 		const size_t feature_length = (size_t) (feature_end - feature_start);
119*2b54f0dbSXin Li 
120*2b54f0dbSXin Li 		switch (feature_length) {
121*2b54f0dbSXin Li 			case 2:
122*2b54f0dbSXin Li 				if (memcmp(feature_start, "fp", feature_length) == 0) {
123*2b54f0dbSXin Li #if CPUINFO_ARCH_ARM64
124*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_FP;
125*2b54f0dbSXin Li #endif
126*2b54f0dbSXin Li #if CPUINFO_ARCH_ARM
127*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "wp", feature_length) == 0) {
128*2b54f0dbSXin Li 					/*
129*2b54f0dbSXin Li 					 * Some AArch64 kernels, including the one on Nexus 5X,
130*2b54f0dbSXin Li 					 * erroneously report "swp" as "wp" to AArch32 programs
131*2b54f0dbSXin Li 					 */
132*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_SWP;
133*2b54f0dbSXin Li #endif
134*2b54f0dbSXin Li 				} else {
135*2b54f0dbSXin Li 					goto unexpected;
136*2b54f0dbSXin Li 				}
137*2b54f0dbSXin Li 				break;
138*2b54f0dbSXin Li 			case 3:
139*2b54f0dbSXin Li 				if (memcmp(feature_start, "aes", feature_length) == 0) {
140*2b54f0dbSXin Li 					#if CPUINFO_ARCH_ARM
141*2b54f0dbSXin Li 						processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_AES;
142*2b54f0dbSXin Li 					#elif CPUINFO_ARCH_ARM64
143*2b54f0dbSXin Li 						processor->features |= CPUINFO_ARM_LINUX_FEATURE_AES;
144*2b54f0dbSXin Li 					#endif
145*2b54f0dbSXin Li #if CPUINFO_ARCH_ARM
146*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "swp", feature_length) == 0) {
147*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_SWP;
148*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "fpa", feature_length) == 0) {
149*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_FPA;
150*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "vfp", feature_length) == 0) {
151*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_VFP;
152*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "tls", feature_length) == 0) {
153*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_TLS;
154*2b54f0dbSXin Li #endif /* CPUINFO_ARCH_ARM */
155*2b54f0dbSXin Li 				} else {
156*2b54f0dbSXin Li 					goto unexpected;
157*2b54f0dbSXin Li 				}
158*2b54f0dbSXin Li 				break;
159*2b54f0dbSXin Li 			case 4:
160*2b54f0dbSXin Li 				if (memcmp(feature_start, "sha1", feature_length) == 0) {
161*2b54f0dbSXin Li 					#if CPUINFO_ARCH_ARM
162*2b54f0dbSXin Li 						processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_SHA1;
163*2b54f0dbSXin Li 					#elif CPUINFO_ARCH_ARM64
164*2b54f0dbSXin Li 						processor->features |= CPUINFO_ARM_LINUX_FEATURE_SHA1;
165*2b54f0dbSXin Li 					#endif
166*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "sha2", feature_length) == 0) {
167*2b54f0dbSXin Li 					#if CPUINFO_ARCH_ARM
168*2b54f0dbSXin Li 						processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_SHA2;
169*2b54f0dbSXin Li 					#elif CPUINFO_ARCH_ARM64
170*2b54f0dbSXin Li 						processor->features |= CPUINFO_ARM_LINUX_FEATURE_SHA2;
171*2b54f0dbSXin Li 					#endif
172*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "fphp", feature_length) == 0) {
173*2b54f0dbSXin Li 					#if CPUINFO_ARCH_ARM64
174*2b54f0dbSXin Li 						processor->features |= CPUINFO_ARM_LINUX_FEATURE_FPHP;
175*2b54f0dbSXin Li 					#endif
176*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "fcma", feature_length) == 0) {
177*2b54f0dbSXin Li 					#if CPUINFO_ARCH_ARM64
178*2b54f0dbSXin Li 						processor->features |= CPUINFO_ARM_LINUX_FEATURE_FCMA;
179*2b54f0dbSXin Li 					#endif
180*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "i8mm", feature_length) == 0) {
181*2b54f0dbSXin Li 					#if CPUINFO_ARCH_ARM64
182*2b54f0dbSXin Li 						processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_I8MM;
183*2b54f0dbSXin Li 					#endif
184*2b54f0dbSXin Li #if CPUINFO_ARCH_ARM
185*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "half", feature_length) == 0) {
186*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_HALF;
187*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "edsp", feature_length) == 0) {
188*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_EDSP;
189*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "java", feature_length) == 0) {
190*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_JAVA;
191*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "neon", feature_length) == 0) {
192*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_NEON;
193*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "lpae", feature_length) == 0) {
194*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_LPAE;
195*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "tlsi", feature_length) == 0) {
196*2b54f0dbSXin Li 					/*
197*2b54f0dbSXin Li 					 * Some AArch64 kernels, including the one on Nexus 5X,
198*2b54f0dbSXin Li 					 * erroneously report "tls" as "tlsi" to AArch32 programs
199*2b54f0dbSXin Li 					 */
200*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_TLS;
201*2b54f0dbSXin Li #endif /* CPUINFO_ARCH_ARM */
202*2b54f0dbSXin Li 				} else {
203*2b54f0dbSXin Li 					goto unexpected;
204*2b54f0dbSXin Li 				}
205*2b54f0dbSXin Li 				break;
206*2b54f0dbSXin Li 			case 5:
207*2b54f0dbSXin Li 				if (memcmp(feature_start, "pmull", feature_length) == 0) {
208*2b54f0dbSXin Li 					#if CPUINFO_ARCH_ARM
209*2b54f0dbSXin Li 						processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_PMULL;
210*2b54f0dbSXin Li 					#elif CPUINFO_ARCH_ARM64
211*2b54f0dbSXin Li 						processor->features |= CPUINFO_ARM_LINUX_FEATURE_PMULL;
212*2b54f0dbSXin Li 					#endif
213*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "crc32", feature_length) == 0) {
214*2b54f0dbSXin Li 					#if CPUINFO_ARCH_ARM
215*2b54f0dbSXin Li 						processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_CRC32;
216*2b54f0dbSXin Li 					#elif CPUINFO_ARCH_ARM64
217*2b54f0dbSXin Li 						processor->features |= CPUINFO_ARM_LINUX_FEATURE_CRC32;
218*2b54f0dbSXin Li 					#endif
219*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "asimd", feature_length) == 0) {
220*2b54f0dbSXin Li 					#if CPUINFO_ARCH_ARM64
221*2b54f0dbSXin Li 						processor->features |= CPUINFO_ARM_LINUX_FEATURE_ASIMD;
222*2b54f0dbSXin Li 					#endif
223*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "cpuid", feature_length) == 0) {
224*2b54f0dbSXin Li 					#if CPUINFO_ARCH_ARM64
225*2b54f0dbSXin Li 						processor->features |= CPUINFO_ARM_LINUX_FEATURE_CPUID;
226*2b54f0dbSXin Li 					#endif
227*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "jscvt", feature_length) == 0) {
228*2b54f0dbSXin Li 					#if CPUINFO_ARCH_ARM64
229*2b54f0dbSXin Li 						processor->features |= CPUINFO_ARM_LINUX_FEATURE_JSCVT;
230*2b54f0dbSXin Li 					#endif
231*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "lrcpc", feature_length) == 0) {
232*2b54f0dbSXin Li 					#if CPUINFO_ARCH_ARM64
233*2b54f0dbSXin Li 						processor->features |= CPUINFO_ARM_LINUX_FEATURE_LRCPC;
234*2b54f0dbSXin Li 					#endif
235*2b54f0dbSXin Li #if CPUINFO_ARCH_ARM
236*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "thumb", feature_length) == 0) {
237*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_THUMB;
238*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "26bit", feature_length) == 0) {
239*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_26BIT;
240*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "vfpv3", feature_length) == 0) {
241*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_VFPV3;
242*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "vfpv4", feature_length) == 0) {
243*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_VFPV4;
244*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "idiva", feature_length) == 0) {
245*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_IDIVA;
246*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "idivt", feature_length) == 0) {
247*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_IDIVT;
248*2b54f0dbSXin Li #endif /* CPUINFO_ARCH_ARM */
249*2b54f0dbSXin Li 				} else {
250*2b54f0dbSXin Li 					goto unexpected;
251*2b54f0dbSXin Li 				}
252*2b54f0dbSXin Li  				break;
253*2b54f0dbSXin Li #if CPUINFO_ARCH_ARM
254*2b54f0dbSXin Li 			case 6:
255*2b54f0dbSXin Li 				if (memcmp(feature_start, "iwmmxt", feature_length) == 0) {
256*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_IWMMXT;
257*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "crunch", feature_length) == 0) {
258*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_CRUNCH;
259*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "vfpd32", feature_length) == 0) {
260*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_VFPD32;
261*2b54f0dbSXin Li 				} else {
262*2b54f0dbSXin Li 					goto unexpected;
263*2b54f0dbSXin Li 				}
264*2b54f0dbSXin Li 				break;
265*2b54f0dbSXin Li #endif /* CPUINFO_ARCH_ARM */
266*2b54f0dbSXin Li 			case 7:
267*2b54f0dbSXin Li 				if (memcmp(feature_start, "evtstrm", feature_length) == 0) {
268*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_EVTSTRM;
269*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "atomics", feature_length) == 0) {
270*2b54f0dbSXin Li 					#if CPUINFO_ARCH_ARM64
271*2b54f0dbSXin Li 						processor->features |= CPUINFO_ARM_LINUX_FEATURE_ATOMICS;
272*2b54f0dbSXin Li 					#endif
273*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "asimdhp", feature_length) == 0) {
274*2b54f0dbSXin Li 					#if CPUINFO_ARCH_ARM64
275*2b54f0dbSXin Li 						processor->features |= CPUINFO_ARM_LINUX_FEATURE_ASIMDHP;
276*2b54f0dbSXin Li 					#endif
277*2b54f0dbSXin Li #if CPUINFO_ARCH_ARM
278*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "thumbee", feature_length) == 0) {
279*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_THUMBEE;
280*2b54f0dbSXin Li #endif /* CPUINFO_ARCH_ARM */
281*2b54f0dbSXin Li 				} else {
282*2b54f0dbSXin Li 					goto unexpected;
283*2b54f0dbSXin Li 				}
284*2b54f0dbSXin Li 				break;
285*2b54f0dbSXin Li 			case 8:
286*2b54f0dbSXin Li 				if (memcmp(feature_start, "asimdrdm", feature_length) == 0) {
287*2b54f0dbSXin Li 					#if CPUINFO_ARCH_ARM64
288*2b54f0dbSXin Li 						processor->features |= CPUINFO_ARM_LINUX_FEATURE_ASIMDRDM;
289*2b54f0dbSXin Li 					#endif
290*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "asimdfhm", feature_length) == 0) {
291*2b54f0dbSXin Li 					#if CPUINFO_ARCH_ARM64
292*2b54f0dbSXin Li 						processor->features |= CPUINFO_ARM_LINUX_FEATURE_ASIMDFHM;
293*2b54f0dbSXin Li 					#endif
294*2b54f0dbSXin Li #if CPUINFO_ARCH_ARM
295*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "fastmult", feature_length) == 0) {
296*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_FASTMULT;
297*2b54f0dbSXin Li 				} else if (memcmp(feature_start, "vfpv3d16", feature_length) == 0) {
298*2b54f0dbSXin Li 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_VFPV3D16;
299*2b54f0dbSXin Li #endif /* CPUINFO_ARCH_ARM */
300*2b54f0dbSXin Li 				} else {
301*2b54f0dbSXin Li 					goto unexpected;
302*2b54f0dbSXin Li 				}
303*2b54f0dbSXin Li 				break;
304*2b54f0dbSXin Li 			default:
305*2b54f0dbSXin Li 			unexpected:
306*2b54f0dbSXin Li 				cpuinfo_log_warning("unexpected /proc/cpuinfo feature \"%.*s\" is ignored",
307*2b54f0dbSXin Li 					(int) feature_length, feature_start);
308*2b54f0dbSXin Li 				break;
309*2b54f0dbSXin Li 		}
310*2b54f0dbSXin Li 		feature_start = feature_end;
311*2b54f0dbSXin Li 		for (; feature_start != features_end; feature_start++) {
312*2b54f0dbSXin Li 			if (*feature_start != ' ') {
313*2b54f0dbSXin Li 				break;
314*2b54f0dbSXin Li 			}
315*2b54f0dbSXin Li 		}
316*2b54f0dbSXin Li 	} while (feature_start != feature_end);
317*2b54f0dbSXin Li }
318*2b54f0dbSXin Li 
parse_cpu_architecture(const char * cpu_architecture_start,const char * cpu_architecture_end,struct cpuinfo_arm_linux_processor processor[restrict static1])319*2b54f0dbSXin Li static void parse_cpu_architecture(
320*2b54f0dbSXin Li 	const char* cpu_architecture_start,
321*2b54f0dbSXin Li 	const char* cpu_architecture_end,
322*2b54f0dbSXin Li 	struct cpuinfo_arm_linux_processor processor[restrict static 1])
323*2b54f0dbSXin Li {
324*2b54f0dbSXin Li 	const size_t cpu_architecture_length = (size_t) (cpu_architecture_end - cpu_architecture_start);
325*2b54f0dbSXin Li 	/* Early AArch64 kernels report "CPU architecture: AArch64" instead of a numeric value 8 */
326*2b54f0dbSXin Li 	if (cpu_architecture_length == 7) {
327*2b54f0dbSXin Li 		if (memcmp(cpu_architecture_start, "AArch64", cpu_architecture_length) == 0) {
328*2b54f0dbSXin Li 			processor->midr = midr_set_architecture(processor->midr, UINT32_C(0xF));
329*2b54f0dbSXin Li 			processor->architecture_version = 8;
330*2b54f0dbSXin Li 			processor->flags |= CPUINFO_ARM_LINUX_VALID_ARCHITECTURE | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
331*2b54f0dbSXin Li 			return;
332*2b54f0dbSXin Li 		}
333*2b54f0dbSXin Li 	}
334*2b54f0dbSXin Li 
335*2b54f0dbSXin Li 
336*2b54f0dbSXin Li 	uint32_t architecture = 0;
337*2b54f0dbSXin Li 	const char* cpu_architecture_ptr = cpu_architecture_start;
338*2b54f0dbSXin Li 	for (; cpu_architecture_ptr != cpu_architecture_end; cpu_architecture_ptr++) {
339*2b54f0dbSXin Li 		const uint32_t digit = (*cpu_architecture_ptr) - '0';
340*2b54f0dbSXin Li 
341*2b54f0dbSXin Li 		/* Verify that CPU architecture is a decimal number */
342*2b54f0dbSXin Li 		if (digit >= 10) {
343*2b54f0dbSXin Li 			break;
344*2b54f0dbSXin Li 		}
345*2b54f0dbSXin Li 
346*2b54f0dbSXin Li 		architecture = architecture * 10 + digit;
347*2b54f0dbSXin Li 	}
348*2b54f0dbSXin Li 
349*2b54f0dbSXin Li 	if (cpu_architecture_ptr == cpu_architecture_start) {
350*2b54f0dbSXin Li 		cpuinfo_log_warning("CPU architecture %.*s in /proc/cpuinfo is ignored due to non-digit at the beginning of the string",
351*2b54f0dbSXin Li 			(int) cpu_architecture_length, cpu_architecture_start);
352*2b54f0dbSXin Li 	} else {
353*2b54f0dbSXin Li 		if (architecture != 0) {
354*2b54f0dbSXin Li 			processor->architecture_version = architecture;
355*2b54f0dbSXin Li 			processor->flags |= CPUINFO_ARM_LINUX_VALID_ARCHITECTURE | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
356*2b54f0dbSXin Li 
357*2b54f0dbSXin Li 			for (; cpu_architecture_ptr != cpu_architecture_end; cpu_architecture_ptr++) {
358*2b54f0dbSXin Li 				const char feature = *cpu_architecture_ptr;
359*2b54f0dbSXin Li 				switch (feature) {
360*2b54f0dbSXin Li #if CPUINFO_ARCH_ARM
361*2b54f0dbSXin Li 					case 'T':
362*2b54f0dbSXin Li 						processor->architecture_flags |= CPUINFO_ARM_LINUX_ARCH_T;
363*2b54f0dbSXin Li 						break;
364*2b54f0dbSXin Li 					case 'E':
365*2b54f0dbSXin Li 						processor->architecture_flags |= CPUINFO_ARM_LINUX_ARCH_E;
366*2b54f0dbSXin Li 						break;
367*2b54f0dbSXin Li 					case 'J':
368*2b54f0dbSXin Li 						processor->architecture_flags |= CPUINFO_ARM_LINUX_ARCH_J;
369*2b54f0dbSXin Li 						break;
370*2b54f0dbSXin Li #endif /* CPUINFO_ARCH_ARM */
371*2b54f0dbSXin Li 					case ' ':
372*2b54f0dbSXin Li 					case '\t':
373*2b54f0dbSXin Li 						/* Ignore whitespace at the end */
374*2b54f0dbSXin Li 						break;
375*2b54f0dbSXin Li 					default:
376*2b54f0dbSXin Li 						cpuinfo_log_warning("skipped unknown architectural feature '%c' for ARMv%"PRIu32,
377*2b54f0dbSXin Li 							feature, architecture);
378*2b54f0dbSXin Li 						break;
379*2b54f0dbSXin Li 				}
380*2b54f0dbSXin Li 			}
381*2b54f0dbSXin Li 		} else {
382*2b54f0dbSXin Li 			cpuinfo_log_warning("CPU architecture %.*s in /proc/cpuinfo is ignored due to invalid value (0)",
383*2b54f0dbSXin Li 				(int) cpu_architecture_length, cpu_architecture_start);
384*2b54f0dbSXin Li 		}
385*2b54f0dbSXin Li 	}
386*2b54f0dbSXin Li 
387*2b54f0dbSXin Li 	uint32_t midr_architecture = UINT32_C(0xF);
388*2b54f0dbSXin Li #if CPUINFO_ARCH_ARM
389*2b54f0dbSXin Li 	switch (processor->architecture_version) {
390*2b54f0dbSXin Li 		case 6:
391*2b54f0dbSXin Li 			midr_architecture = UINT32_C(0x7); /* ARMv6 */
392*2b54f0dbSXin Li 			break;
393*2b54f0dbSXin Li 		case 5:
394*2b54f0dbSXin Li 			if ((processor->architecture_flags & CPUINFO_ARM_LINUX_ARCH_TEJ) == CPUINFO_ARM_LINUX_ARCH_TEJ) {
395*2b54f0dbSXin Li 				midr_architecture = UINT32_C(0x6); /* ARMv5TEJ */
396*2b54f0dbSXin Li 			} else if ((processor->architecture_flags & CPUINFO_ARM_LINUX_ARCH_TE) == CPUINFO_ARM_LINUX_ARCH_TE) {
397*2b54f0dbSXin Li 				midr_architecture = UINT32_C(0x5); /* ARMv5TE */
398*2b54f0dbSXin Li 			} else {
399*2b54f0dbSXin Li 				midr_architecture = UINT32_C(0x4); /* ARMv5T */
400*2b54f0dbSXin Li 			}
401*2b54f0dbSXin Li 			break;
402*2b54f0dbSXin Li 	}
403*2b54f0dbSXin Li #endif
404*2b54f0dbSXin Li 	processor->midr = midr_set_architecture(processor->midr, midr_architecture);
405*2b54f0dbSXin Li }
406*2b54f0dbSXin Li 
parse_cpu_part(const char * cpu_part_start,const char * cpu_part_end,struct cpuinfo_arm_linux_processor processor[restrict static1])407*2b54f0dbSXin Li static void parse_cpu_part(
408*2b54f0dbSXin Li 	const char* cpu_part_start,
409*2b54f0dbSXin Li 	const char* cpu_part_end,
410*2b54f0dbSXin Li 	struct cpuinfo_arm_linux_processor processor[restrict static 1])
411*2b54f0dbSXin Li {
412*2b54f0dbSXin Li 	const size_t cpu_part_length = (size_t) (cpu_part_end - cpu_part_start);
413*2b54f0dbSXin Li 
414*2b54f0dbSXin Li 	/*
415*2b54f0dbSXin Li 	 * CPU part should contain hex prefix (0x) and one to three hex digits.
416*2b54f0dbSXin Li 	 * I have never seen less than three digits as a value of this field,
417*2b54f0dbSXin Li 	 * but I don't think it is impossible to see such values in future.
418*2b54f0dbSXin Li 	 * Value can not contain more than three hex digits since
419*2b54f0dbSXin Li 	 * Main ID Register (MIDR) assigns only a 12-bit value for CPU part.
420*2b54f0dbSXin Li 	 */
421*2b54f0dbSXin Li 	if (cpu_part_length < 3 || cpu_part_length > 5) {
422*2b54f0dbSXin Li 		cpuinfo_log_warning("CPU part %.*s in /proc/cpuinfo is ignored due to unexpected length (%zu)",
423*2b54f0dbSXin Li 			(int) cpu_part_length, cpu_part_start, cpu_part_length);
424*2b54f0dbSXin Li 		return;
425*2b54f0dbSXin Li 	}
426*2b54f0dbSXin Li 
427*2b54f0dbSXin Li 	/* Verify the presence of hex prefix */
428*2b54f0dbSXin Li 	if (cpu_part_start[0] != '0' || cpu_part_start[1] != 'x') {
429*2b54f0dbSXin Li 		cpuinfo_log_warning("CPU part %.*s in /proc/cpuinfo is ignored due to lack of 0x prefix",
430*2b54f0dbSXin Li 			(int) cpu_part_length, cpu_part_start);
431*2b54f0dbSXin Li 		return;
432*2b54f0dbSXin Li 	}
433*2b54f0dbSXin Li 
434*2b54f0dbSXin Li 	/* Verify that characters after hex prefix are hexadecimal digits and decode them */
435*2b54f0dbSXin Li 	uint32_t cpu_part = 0;
436*2b54f0dbSXin Li 	for (const char* digit_ptr = cpu_part_start + 2; digit_ptr != cpu_part_end; digit_ptr++) {
437*2b54f0dbSXin Li 		const char digit_char = *digit_ptr;
438*2b54f0dbSXin Li 		uint32_t digit;
439*2b54f0dbSXin Li 		if (digit_char >= '0' && digit_char <= '9') {
440*2b54f0dbSXin Li 			digit = digit_char - '0';
441*2b54f0dbSXin Li 		} else if ((uint32_t) (digit_char - 'A') < 6) {
442*2b54f0dbSXin Li 			digit = 10 + (digit_char - 'A');
443*2b54f0dbSXin Li 		} else if ((uint32_t) (digit_char - 'a') < 6) {
444*2b54f0dbSXin Li 			digit = 10 + (digit_char - 'a');
445*2b54f0dbSXin Li 		} else {
446*2b54f0dbSXin Li 			cpuinfo_log_warning("CPU part %.*s in /proc/cpuinfo is ignored due to unexpected non-hex character %c at offset %zu",
447*2b54f0dbSXin Li 				(int) cpu_part_length, cpu_part_start, digit_char, (size_t) (digit_ptr - cpu_part_start));
448*2b54f0dbSXin Li 			return;
449*2b54f0dbSXin Li 		}
450*2b54f0dbSXin Li 		cpu_part = cpu_part * 16 + digit;
451*2b54f0dbSXin Li 	}
452*2b54f0dbSXin Li 
453*2b54f0dbSXin Li 	processor->midr = midr_set_part(processor->midr, cpu_part);
454*2b54f0dbSXin Li 	processor->flags |= CPUINFO_ARM_LINUX_VALID_PART | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
455*2b54f0dbSXin Li }
456*2b54f0dbSXin Li 
parse_cpu_implementer(const char * cpu_implementer_start,const char * cpu_implementer_end,struct cpuinfo_arm_linux_processor processor[restrict static1])457*2b54f0dbSXin Li static void parse_cpu_implementer(
458*2b54f0dbSXin Li 	const char* cpu_implementer_start,
459*2b54f0dbSXin Li 	const char* cpu_implementer_end,
460*2b54f0dbSXin Li 	struct cpuinfo_arm_linux_processor processor[restrict static 1])
461*2b54f0dbSXin Li {
462*2b54f0dbSXin Li 	const size_t cpu_implementer_length = cpu_implementer_end - cpu_implementer_start;
463*2b54f0dbSXin Li 
464*2b54f0dbSXin Li 	/*
465*2b54f0dbSXin Li 	 * Value should contain hex prefix (0x) and one or two hex digits.
466*2b54f0dbSXin Li 	 * I have never seen single hex digit as a value of this field,
467*2b54f0dbSXin Li 	 * but I don't think it is impossible in future.
468*2b54f0dbSXin Li 	 * Value can not contain more than two hex digits since
469*2b54f0dbSXin Li 	 * Main ID Register (MIDR) assigns only an 8-bit value for CPU implementer.
470*2b54f0dbSXin Li 	 */
471*2b54f0dbSXin Li 	switch (cpu_implementer_length) {
472*2b54f0dbSXin Li 		case 3:
473*2b54f0dbSXin Li 		case 4:
474*2b54f0dbSXin Li 			break;
475*2b54f0dbSXin Li 		default:
476*2b54f0dbSXin Li 		cpuinfo_log_warning("CPU implementer %.*s in /proc/cpuinfo is ignored due to unexpected length (%zu)",
477*2b54f0dbSXin Li 			(int) cpu_implementer_length, cpu_implementer_start, cpu_implementer_length);
478*2b54f0dbSXin Li 		return;
479*2b54f0dbSXin Li 	}
480*2b54f0dbSXin Li 
481*2b54f0dbSXin Li 	/* Verify the presence of hex prefix */
482*2b54f0dbSXin Li 	if (cpu_implementer_start[0] != '0' || cpu_implementer_start[1] != 'x') {
483*2b54f0dbSXin Li 		cpuinfo_log_warning("CPU implementer %.*s in /proc/cpuinfo is ignored due to lack of 0x prefix",
484*2b54f0dbSXin Li 			(int) cpu_implementer_length, cpu_implementer_start);
485*2b54f0dbSXin Li 		return;
486*2b54f0dbSXin Li 	}
487*2b54f0dbSXin Li 
488*2b54f0dbSXin Li 	/* Verify that characters after hex prefix are hexadecimal digits and decode them */
489*2b54f0dbSXin Li 	uint32_t cpu_implementer = 0;
490*2b54f0dbSXin Li 	for (const char* digit_ptr = cpu_implementer_start + 2; digit_ptr != cpu_implementer_end; digit_ptr++) {
491*2b54f0dbSXin Li 		const char digit_char = *digit_ptr;
492*2b54f0dbSXin Li 		uint32_t digit;
493*2b54f0dbSXin Li 		if (digit_char >= '0' && digit_char <= '9') {
494*2b54f0dbSXin Li 			digit = digit_char - '0';
495*2b54f0dbSXin Li 		} else if ((uint32_t) (digit_char - 'A') < 6) {
496*2b54f0dbSXin Li 			digit = 10 + (digit_char - 'A');
497*2b54f0dbSXin Li 		} else if ((uint32_t) (digit_char - 'a') < 6) {
498*2b54f0dbSXin Li 			digit = 10 + (digit_char - 'a');
499*2b54f0dbSXin Li 		} else {
500*2b54f0dbSXin Li 			cpuinfo_log_warning("CPU implementer %.*s in /proc/cpuinfo is ignored due to unexpected non-hex character '%c' at offset %zu",
501*2b54f0dbSXin Li 				(int) cpu_implementer_length, cpu_implementer_start, digit_char, (size_t) (digit_ptr - cpu_implementer_start));
502*2b54f0dbSXin Li 			return;
503*2b54f0dbSXin Li 		}
504*2b54f0dbSXin Li 		cpu_implementer = cpu_implementer * 16 + digit;
505*2b54f0dbSXin Li 	}
506*2b54f0dbSXin Li 
507*2b54f0dbSXin Li 	processor->midr = midr_set_implementer(processor->midr, cpu_implementer);
508*2b54f0dbSXin Li 	processor->flags |= CPUINFO_ARM_LINUX_VALID_IMPLEMENTER | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
509*2b54f0dbSXin Li }
510*2b54f0dbSXin Li 
parse_cpu_variant(const char * cpu_variant_start,const char * cpu_variant_end,struct cpuinfo_arm_linux_processor processor[restrict static1])511*2b54f0dbSXin Li static void parse_cpu_variant(
512*2b54f0dbSXin Li 	const char* cpu_variant_start,
513*2b54f0dbSXin Li 	const char* cpu_variant_end,
514*2b54f0dbSXin Li 	struct cpuinfo_arm_linux_processor processor[restrict static 1])
515*2b54f0dbSXin Li {
516*2b54f0dbSXin Li 	const size_t cpu_variant_length = cpu_variant_end - cpu_variant_start;
517*2b54f0dbSXin Li 
518*2b54f0dbSXin Li 	/*
519*2b54f0dbSXin Li 	 * Value should contain hex prefix (0x) and one hex digit.
520*2b54f0dbSXin Li 	 * Value can not contain more than one hex digits since
521*2b54f0dbSXin Li 	 * Main ID Register (MIDR) assigns only a 4-bit value for CPU variant.
522*2b54f0dbSXin Li 	 */
523*2b54f0dbSXin Li 	if (cpu_variant_length != 3) {
524*2b54f0dbSXin Li 		cpuinfo_log_warning("CPU variant %.*s in /proc/cpuinfo is ignored due to unexpected length (%zu)",
525*2b54f0dbSXin Li 			(int) cpu_variant_length, cpu_variant_start, cpu_variant_length);
526*2b54f0dbSXin Li 		return;
527*2b54f0dbSXin Li 	}
528*2b54f0dbSXin Li 
529*2b54f0dbSXin Li 	/* Skip if there is no hex prefix (0x) */
530*2b54f0dbSXin Li 	if (cpu_variant_start[0] != '0' || cpu_variant_start[1] != 'x') {
531*2b54f0dbSXin Li 		cpuinfo_log_warning("CPU variant %.*s in /proc/cpuinfo is ignored due to lack of 0x prefix",
532*2b54f0dbSXin Li 			(int) cpu_variant_length, cpu_variant_start);
533*2b54f0dbSXin Li 		return;
534*2b54f0dbSXin Li 	}
535*2b54f0dbSXin Li 
536*2b54f0dbSXin Li 	/* Check if the value after hex prefix is indeed a hex digit and decode it. */
537*2b54f0dbSXin Li 	const char digit_char = cpu_variant_start[2];
538*2b54f0dbSXin Li 	uint32_t cpu_variant;
539*2b54f0dbSXin Li 	if ((uint32_t) (digit_char - '0') < 10) {
540*2b54f0dbSXin Li 		cpu_variant = (uint32_t) (digit_char - '0');
541*2b54f0dbSXin Li 	} else if ((uint32_t) (digit_char - 'A') < 6) {
542*2b54f0dbSXin Li 		cpu_variant = 10 + (uint32_t) (digit_char - 'A');
543*2b54f0dbSXin Li 	} else if ((uint32_t) (digit_char - 'a') < 6) {
544*2b54f0dbSXin Li 		cpu_variant = 10 + (uint32_t) (digit_char - 'a');
545*2b54f0dbSXin Li 	} else {
546*2b54f0dbSXin Li 		cpuinfo_log_warning("CPU variant %.*s in /proc/cpuinfo is ignored due to unexpected non-hex character '%c'",
547*2b54f0dbSXin Li 			(int) cpu_variant_length, cpu_variant_start, digit_char);
548*2b54f0dbSXin Li 		return;
549*2b54f0dbSXin Li 	}
550*2b54f0dbSXin Li 
551*2b54f0dbSXin Li 	processor->midr = midr_set_variant(processor->midr, cpu_variant);
552*2b54f0dbSXin Li 	processor->flags |= CPUINFO_ARM_LINUX_VALID_VARIANT | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
553*2b54f0dbSXin Li }
554*2b54f0dbSXin Li 
parse_cpu_revision(const char * cpu_revision_start,const char * cpu_revision_end,struct cpuinfo_arm_linux_processor processor[restrict static1])555*2b54f0dbSXin Li static void parse_cpu_revision(
556*2b54f0dbSXin Li 	const char* cpu_revision_start,
557*2b54f0dbSXin Li 	const char* cpu_revision_end,
558*2b54f0dbSXin Li 	struct cpuinfo_arm_linux_processor processor[restrict static 1])
559*2b54f0dbSXin Li {
560*2b54f0dbSXin Li 	uint32_t cpu_revision = 0;
561*2b54f0dbSXin Li 	for (const char* digit_ptr = cpu_revision_start; digit_ptr != cpu_revision_end; digit_ptr++) {
562*2b54f0dbSXin Li 		const uint32_t digit = (uint32_t) (*digit_ptr - '0');
563*2b54f0dbSXin Li 
564*2b54f0dbSXin Li 		/* Verify that the character in CPU revision is a decimal digit */
565*2b54f0dbSXin Li 		if (digit >= 10) {
566*2b54f0dbSXin Li 			cpuinfo_log_warning("CPU revision %.*s in /proc/cpuinfo is ignored due to unexpected non-digit character '%c' at offset %zu",
567*2b54f0dbSXin Li 				(int) (cpu_revision_end - cpu_revision_start), cpu_revision_start,
568*2b54f0dbSXin Li 				*digit_ptr, (size_t) (digit_ptr - cpu_revision_start));
569*2b54f0dbSXin Li 			return;
570*2b54f0dbSXin Li 		}
571*2b54f0dbSXin Li 
572*2b54f0dbSXin Li 		cpu_revision = cpu_revision * 10 + digit;
573*2b54f0dbSXin Li 	}
574*2b54f0dbSXin Li 
575*2b54f0dbSXin Li 	processor->midr = midr_set_revision(processor->midr, cpu_revision);
576*2b54f0dbSXin Li 	processor->flags |= CPUINFO_ARM_LINUX_VALID_REVISION | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
577*2b54f0dbSXin Li }
578*2b54f0dbSXin Li 
579*2b54f0dbSXin Li #if CPUINFO_ARCH_ARM
580*2b54f0dbSXin Li /*
581*2b54f0dbSXin Li  * Decode one of the cache-related numbers reported by Linux kernel
582*2b54f0dbSXin Li  * for pre-ARMv7 architecture.
583*2b54f0dbSXin Li  * An example cache-related information in /proc/cpuinfo:
584*2b54f0dbSXin Li  *
585*2b54f0dbSXin Li  *      I size          : 32768
586*2b54f0dbSXin Li  *      I assoc         : 4
587*2b54f0dbSXin Li  *      I line length   : 32
588*2b54f0dbSXin Li  *      I sets          : 256
589*2b54f0dbSXin Li  *      D size          : 16384
590*2b54f0dbSXin Li  *      D assoc         : 4
591*2b54f0dbSXin Li  *      D line length   : 32
592*2b54f0dbSXin Li  *      D sets          : 128
593*2b54f0dbSXin Li  *
594*2b54f0dbSXin Li  */
parse_cache_number(const char * number_start,const char * number_end,const char * number_name,uint32_t number_ptr[restrict static1],uint32_t flags[restrict static1],uint32_t number_mask)595*2b54f0dbSXin Li static void parse_cache_number(
596*2b54f0dbSXin Li 	const char* number_start,
597*2b54f0dbSXin Li 	const char* number_end,
598*2b54f0dbSXin Li 	const char* number_name,
599*2b54f0dbSXin Li 	uint32_t number_ptr[restrict static 1],
600*2b54f0dbSXin Li 	uint32_t flags[restrict static 1],
601*2b54f0dbSXin Li 	uint32_t number_mask)
602*2b54f0dbSXin Li {
603*2b54f0dbSXin Li 	uint32_t number = 0;
604*2b54f0dbSXin Li 	for (const char* digit_ptr = number_start; digit_ptr != number_end; digit_ptr++) {
605*2b54f0dbSXin Li 		const uint32_t digit = *digit_ptr - '0';
606*2b54f0dbSXin Li 		if (digit >= 10) {
607*2b54f0dbSXin Li 			cpuinfo_log_warning("%s %.*s in /proc/cpuinfo is ignored due to unexpected non-digit character '%c' at offset %zu",
608*2b54f0dbSXin Li 				number_name, (int) (number_end - number_start), number_start,
609*2b54f0dbSXin Li 				*digit_ptr, (size_t) (digit_ptr - number_start));
610*2b54f0dbSXin Li 			return;
611*2b54f0dbSXin Li 		}
612*2b54f0dbSXin Li 
613*2b54f0dbSXin Li 		number = number * 10 + digit;
614*2b54f0dbSXin Li 	}
615*2b54f0dbSXin Li 
616*2b54f0dbSXin Li 	if (number == 0) {
617*2b54f0dbSXin Li 		cpuinfo_log_warning("%s %.*s in /proc/cpuinfo is ignored due to invalid value of zero reported by the kernel",
618*2b54f0dbSXin Li 			number_name, (int) (number_end - number_start), number_start);
619*2b54f0dbSXin Li 	}
620*2b54f0dbSXin Li 
621*2b54f0dbSXin Li 	/* If the number specifies a cache line size, verify that is a reasonable power of 2 */
622*2b54f0dbSXin Li 	if (number_mask & CPUINFO_ARM_LINUX_VALID_CACHE_LINE) {
623*2b54f0dbSXin Li 		switch (number) {
624*2b54f0dbSXin Li 			case 16:
625*2b54f0dbSXin Li 			case 32:
626*2b54f0dbSXin Li 			case 64:
627*2b54f0dbSXin Li 			case 128:
628*2b54f0dbSXin Li 				break;
629*2b54f0dbSXin Li 			default:
630*2b54f0dbSXin Li 				cpuinfo_log_warning("invalid %s %.*s is ignored: a value of 16, 32, 64, or 128 expected",
631*2b54f0dbSXin Li 					number_name, (int) (number_end - number_start), number_start);
632*2b54f0dbSXin Li 		}
633*2b54f0dbSXin Li 	}
634*2b54f0dbSXin Li 
635*2b54f0dbSXin Li 	*number_ptr = number;
636*2b54f0dbSXin Li 	*flags |= number_mask | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
637*2b54f0dbSXin Li }
638*2b54f0dbSXin Li #endif /* CPUINFO_ARCH_ARM */
639*2b54f0dbSXin Li 
640*2b54f0dbSXin Li struct proc_cpuinfo_parser_state {
641*2b54f0dbSXin Li 	char* hardware;
642*2b54f0dbSXin Li 	char* revision;
643*2b54f0dbSXin Li 	uint32_t processor_index;
644*2b54f0dbSXin Li 	uint32_t max_processors_count;
645*2b54f0dbSXin Li 	struct cpuinfo_arm_linux_processor* processors;
646*2b54f0dbSXin Li 	struct cpuinfo_arm_linux_processor dummy_processor;
647*2b54f0dbSXin Li };
648*2b54f0dbSXin Li 
649*2b54f0dbSXin Li /*
650*2b54f0dbSXin Li  *	Decode a single line of /proc/cpuinfo information.
651*2b54f0dbSXin Li  *	Lines have format <words-with-spaces>[ ]*:[ ]<space-separated words>
652*2b54f0dbSXin Li  *	An example of /proc/cpuinfo (from Pandaboard-ES):
653*2b54f0dbSXin Li  *
654*2b54f0dbSXin Li  *		Processor       : ARMv7 Processor rev 10 (v7l)
655*2b54f0dbSXin Li  *		processor       : 0
656*2b54f0dbSXin Li  *		BogoMIPS        : 1392.74
657*2b54f0dbSXin Li  *
658*2b54f0dbSXin Li  *		processor       : 1
659*2b54f0dbSXin Li  *		BogoMIPS        : 1363.33
660*2b54f0dbSXin Li  *
661*2b54f0dbSXin Li  *		Features        : swp half thumb fastmult vfp edsp thumbee neon vfpv3
662*2b54f0dbSXin Li  *		CPU implementer : 0x41
663*2b54f0dbSXin Li  *		CPU architecture: 7
664*2b54f0dbSXin Li  *		CPU variant     : 0x2
665*2b54f0dbSXin Li  *		CPU part        : 0xc09
666*2b54f0dbSXin Li  *		CPU revision    : 10
667*2b54f0dbSXin Li  *
668*2b54f0dbSXin Li  *		Hardware        : OMAP4 Panda board
669*2b54f0dbSXin Li  *		Revision        : 0020
670*2b54f0dbSXin Li  *		Serial          : 0000000000000000
671*2b54f0dbSXin Li  */
parse_line(const char * line_start,const char * line_end,struct proc_cpuinfo_parser_state state[restrict static1],uint64_t line_number)672*2b54f0dbSXin Li static bool parse_line(
673*2b54f0dbSXin Li 	const char* line_start,
674*2b54f0dbSXin Li 	const char* line_end,
675*2b54f0dbSXin Li 	struct proc_cpuinfo_parser_state state[restrict static 1],
676*2b54f0dbSXin Li 	uint64_t line_number)
677*2b54f0dbSXin Li {
678*2b54f0dbSXin Li 	/* Empty line. Skip. */
679*2b54f0dbSXin Li 	if (line_start == line_end) {
680*2b54f0dbSXin Li 		return true;
681*2b54f0dbSXin Li 	}
682*2b54f0dbSXin Li 
683*2b54f0dbSXin Li 	/* Search for ':' on the line. */
684*2b54f0dbSXin Li 	const char* separator = line_start;
685*2b54f0dbSXin Li 	for (; separator != line_end; separator++) {
686*2b54f0dbSXin Li 		if (*separator == ':') {
687*2b54f0dbSXin Li 			break;
688*2b54f0dbSXin Li 		}
689*2b54f0dbSXin Li 	}
690*2b54f0dbSXin Li 	/* Skip line if no ':' separator was found. */
691*2b54f0dbSXin Li 	if (separator == line_end) {
692*2b54f0dbSXin Li 		cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: key/value separator ':' not found",
693*2b54f0dbSXin Li 			(int) (line_end - line_start), line_start);
694*2b54f0dbSXin Li 		return true;
695*2b54f0dbSXin Li 	}
696*2b54f0dbSXin Li 
697*2b54f0dbSXin Li 	/* Skip trailing spaces in key part. */
698*2b54f0dbSXin Li 	const char* key_end = separator;
699*2b54f0dbSXin Li 	for (; key_end != line_start; key_end--) {
700*2b54f0dbSXin Li 		if (key_end[-1] != ' ' && key_end[-1] != '\t') {
701*2b54f0dbSXin Li 			break;
702*2b54f0dbSXin Li 		}
703*2b54f0dbSXin Li 	}
704*2b54f0dbSXin Li 	/* Skip line if key contains nothing but spaces. */
705*2b54f0dbSXin Li 	if (key_end == line_start) {
706*2b54f0dbSXin Li 		cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: key contains only spaces",
707*2b54f0dbSXin Li 			(int) (line_end - line_start), line_start);
708*2b54f0dbSXin Li 		return true;
709*2b54f0dbSXin Li 	}
710*2b54f0dbSXin Li 
711*2b54f0dbSXin Li 	/* Skip leading spaces in value part. */
712*2b54f0dbSXin Li 	const char* value_start = separator + 1;
713*2b54f0dbSXin Li 	for (; value_start != line_end; value_start++) {
714*2b54f0dbSXin Li 		if (*value_start != ' ') {
715*2b54f0dbSXin Li 			break;
716*2b54f0dbSXin Li 		}
717*2b54f0dbSXin Li 	}
718*2b54f0dbSXin Li 	/* Value part contains nothing but spaces. Skip line. */
719*2b54f0dbSXin Li 	if (value_start == line_end) {
720*2b54f0dbSXin Li 		cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: value contains only spaces",
721*2b54f0dbSXin Li 			(int) (line_end - line_start), line_start);
722*2b54f0dbSXin Li 		return true;
723*2b54f0dbSXin Li 	}
724*2b54f0dbSXin Li 
725*2b54f0dbSXin Li 	/* Skip trailing spaces in value part (if any) */
726*2b54f0dbSXin Li 	const char* value_end = line_end;
727*2b54f0dbSXin Li 	for (; value_end != value_start; value_end--) {
728*2b54f0dbSXin Li 		if (value_end[-1] != ' ') {
729*2b54f0dbSXin Li 			break;
730*2b54f0dbSXin Li 		}
731*2b54f0dbSXin Li 	}
732*2b54f0dbSXin Li 
733*2b54f0dbSXin Li 	const uint32_t processor_index      = state->processor_index;
734*2b54f0dbSXin Li 	const uint32_t max_processors_count = state->max_processors_count;
735*2b54f0dbSXin Li 	struct cpuinfo_arm_linux_processor* processors = state->processors;
736*2b54f0dbSXin Li 	struct cpuinfo_arm_linux_processor* processor  = &state->dummy_processor;
737*2b54f0dbSXin Li 	if (processor_index < max_processors_count) {
738*2b54f0dbSXin Li 		processor = &processors[processor_index];
739*2b54f0dbSXin Li 	}
740*2b54f0dbSXin Li 
741*2b54f0dbSXin Li 	const size_t key_length = key_end - line_start;
742*2b54f0dbSXin Li 	switch (key_length) {
743*2b54f0dbSXin Li 		case 6:
744*2b54f0dbSXin Li 			if (memcmp(line_start, "Serial", key_length) == 0) {
745*2b54f0dbSXin Li 				/* Usually contains just zeros, useless */
746*2b54f0dbSXin Li #if CPUINFO_ARCH_ARM
747*2b54f0dbSXin Li 			} else if (memcmp(line_start, "I size", key_length) == 0) {
748*2b54f0dbSXin Li 				parse_cache_number(value_start, value_end,
749*2b54f0dbSXin Li 					"instruction cache size", &processor->proc_cpuinfo_cache.i_size,
750*2b54f0dbSXin Li 					&processor->flags, CPUINFO_ARM_LINUX_VALID_ICACHE_SIZE);
751*2b54f0dbSXin Li 			} else if (memcmp(line_start, "I sets", key_length) == 0) {
752*2b54f0dbSXin Li 				parse_cache_number(value_start, value_end,
753*2b54f0dbSXin Li 					"instruction cache sets", &processor->proc_cpuinfo_cache.i_sets,
754*2b54f0dbSXin Li 					&processor->flags, CPUINFO_ARM_LINUX_VALID_ICACHE_SETS);
755*2b54f0dbSXin Li 			} else if (memcmp(line_start, "D size", key_length) == 0) {
756*2b54f0dbSXin Li 				parse_cache_number(value_start, value_end,
757*2b54f0dbSXin Li 					"data cache size", &processor->proc_cpuinfo_cache.d_size,
758*2b54f0dbSXin Li 					&processor->flags, CPUINFO_ARM_LINUX_VALID_DCACHE_SIZE);
759*2b54f0dbSXin Li 			} else if (memcmp(line_start, "D sets", key_length) == 0) {
760*2b54f0dbSXin Li 				parse_cache_number(value_start, value_end,
761*2b54f0dbSXin Li 					"data cache sets", &processor->proc_cpuinfo_cache.d_sets,
762*2b54f0dbSXin Li 					&processor->flags, CPUINFO_ARM_LINUX_VALID_DCACHE_SETS);
763*2b54f0dbSXin Li #endif /* CPUINFO_ARCH_ARM */
764*2b54f0dbSXin Li 			} else {
765*2b54f0dbSXin Li 				goto unknown;
766*2b54f0dbSXin Li 			}
767*2b54f0dbSXin Li 			break;
768*2b54f0dbSXin Li #if CPUINFO_ARCH_ARM
769*2b54f0dbSXin Li 		case 7:
770*2b54f0dbSXin Li 			if (memcmp(line_start, "I assoc", key_length) == 0) {
771*2b54f0dbSXin Li 				parse_cache_number(value_start, value_end,
772*2b54f0dbSXin Li 					"instruction cache associativity", &processor->proc_cpuinfo_cache.i_assoc,
773*2b54f0dbSXin Li 					&processor->flags, CPUINFO_ARM_LINUX_VALID_ICACHE_WAYS);
774*2b54f0dbSXin Li 			} else if (memcmp(line_start, "D assoc", key_length) == 0) {
775*2b54f0dbSXin Li 				parse_cache_number(value_start, value_end,
776*2b54f0dbSXin Li 					"data cache associativity", &processor->proc_cpuinfo_cache.d_assoc,
777*2b54f0dbSXin Li 					&processor->flags, CPUINFO_ARM_LINUX_VALID_DCACHE_WAYS);
778*2b54f0dbSXin Li 			} else {
779*2b54f0dbSXin Li 				goto unknown;
780*2b54f0dbSXin Li 			}
781*2b54f0dbSXin Li 			break;
782*2b54f0dbSXin Li #endif /* CPUINFO_ARCH_ARM */
783*2b54f0dbSXin Li 		case 8:
784*2b54f0dbSXin Li 			if (memcmp(line_start, "CPU part", key_length) == 0) {
785*2b54f0dbSXin Li 				parse_cpu_part(value_start, value_end, processor);
786*2b54f0dbSXin Li 			} else if (memcmp(line_start, "Features", key_length) == 0) {
787*2b54f0dbSXin Li 				parse_features(value_start, value_end, processor);
788*2b54f0dbSXin Li 			} else if (memcmp(line_start, "BogoMIPS", key_length) == 0) {
789*2b54f0dbSXin Li 				/* BogoMIPS is useless, don't parse */
790*2b54f0dbSXin Li 			} else if (memcmp(line_start, "Hardware", key_length) == 0) {
791*2b54f0dbSXin Li 				size_t value_length = value_end - value_start;
792*2b54f0dbSXin Li 				if (value_length > CPUINFO_HARDWARE_VALUE_MAX) {
793*2b54f0dbSXin Li 					cpuinfo_log_info(
794*2b54f0dbSXin Li 						"length of Hardware value \"%.*s\" in /proc/cpuinfo exceeds limit (%d): truncating to the limit",
795*2b54f0dbSXin Li 						(int) value_length, value_start, CPUINFO_HARDWARE_VALUE_MAX);
796*2b54f0dbSXin Li 					value_length = CPUINFO_HARDWARE_VALUE_MAX;
797*2b54f0dbSXin Li 				} else {
798*2b54f0dbSXin Li 					state->hardware[value_length] = '\0';
799*2b54f0dbSXin Li 				}
800*2b54f0dbSXin Li 				memcpy(state->hardware, value_start, value_length);
801*2b54f0dbSXin Li 				cpuinfo_log_debug("parsed /proc/cpuinfo Hardware = \"%.*s\"", (int) value_length, value_start);
802*2b54f0dbSXin Li 			} else if (memcmp(line_start, "Revision", key_length) == 0) {
803*2b54f0dbSXin Li 				size_t value_length = value_end - value_start;
804*2b54f0dbSXin Li 				if (value_length > CPUINFO_REVISION_VALUE_MAX) {
805*2b54f0dbSXin Li 					cpuinfo_log_info(
806*2b54f0dbSXin Li 						"length of Revision value \"%.*s\" in /proc/cpuinfo exceeds limit (%d): truncating to the limit",
807*2b54f0dbSXin Li 						(int) value_length, value_start, CPUINFO_REVISION_VALUE_MAX);
808*2b54f0dbSXin Li 					value_length = CPUINFO_REVISION_VALUE_MAX;
809*2b54f0dbSXin Li 				} else {
810*2b54f0dbSXin Li 					state->revision[value_length] = '\0';
811*2b54f0dbSXin Li 				}
812*2b54f0dbSXin Li 				memcpy(state->revision, value_start, value_length);
813*2b54f0dbSXin Li 				cpuinfo_log_debug("parsed /proc/cpuinfo Revision = \"%.*s\"", (int) value_length, value_start);
814*2b54f0dbSXin Li 			} else {
815*2b54f0dbSXin Li 				goto unknown;
816*2b54f0dbSXin Li 			}
817*2b54f0dbSXin Li 			break;
818*2b54f0dbSXin Li 		case 9:
819*2b54f0dbSXin Li 			if (memcmp(line_start, "processor", key_length) == 0) {
820*2b54f0dbSXin Li 				const uint32_t new_processor_index = parse_processor_number(value_start, value_end);
821*2b54f0dbSXin Li 				if (new_processor_index < processor_index) {
822*2b54f0dbSXin Li 					/* Strange: decreasing processor number */
823*2b54f0dbSXin Li 					cpuinfo_log_warning(
824*2b54f0dbSXin Li 						"unexpectedly low processor number %"PRIu32" following processor %"PRIu32" in /proc/cpuinfo",
825*2b54f0dbSXin Li 						new_processor_index, processor_index);
826*2b54f0dbSXin Li 				} else if (new_processor_index > processor_index + 1) {
827*2b54f0dbSXin Li 					/* Strange, but common: skipped processor $(processor_index + 1) */
828*2b54f0dbSXin Li 					cpuinfo_log_info(
829*2b54f0dbSXin Li 						"unexpectedly high processor number %"PRIu32" following processor %"PRIu32" in /proc/cpuinfo",
830*2b54f0dbSXin Li 						new_processor_index, processor_index);
831*2b54f0dbSXin Li 				}
832*2b54f0dbSXin Li 				if (new_processor_index < max_processors_count) {
833*2b54f0dbSXin Li 					/* Record that the processor was mentioned in /proc/cpuinfo */
834*2b54f0dbSXin Li 					processors[new_processor_index].flags |= CPUINFO_ARM_LINUX_VALID_PROCESSOR;
835*2b54f0dbSXin Li 				} else {
836*2b54f0dbSXin Li 					/* Log and ignore processor */
837*2b54f0dbSXin Li 					cpuinfo_log_warning("processor %"PRIu32" in /proc/cpuinfo is ignored: index exceeds system limit %"PRIu32,
838*2b54f0dbSXin Li 						new_processor_index, max_processors_count - 1);
839*2b54f0dbSXin Li 				}
840*2b54f0dbSXin Li 				state->processor_index = new_processor_index;
841*2b54f0dbSXin Li 				return true;
842*2b54f0dbSXin Li 			} else if (memcmp(line_start, "Processor", key_length) == 0) {
843*2b54f0dbSXin Li 				/* TODO: parse to fix misreported architecture, similar to Android's cpufeatures */
844*2b54f0dbSXin Li 			} else {
845*2b54f0dbSXin Li 				goto unknown;
846*2b54f0dbSXin Li 			}
847*2b54f0dbSXin Li 			break;
848*2b54f0dbSXin Li 		case 11:
849*2b54f0dbSXin Li 			if (memcmp(line_start, "CPU variant", key_length) == 0) {
850*2b54f0dbSXin Li 				parse_cpu_variant(value_start, value_end, processor);
851*2b54f0dbSXin Li 			} else {
852*2b54f0dbSXin Li 				goto unknown;
853*2b54f0dbSXin Li 			}
854*2b54f0dbSXin Li 			break;
855*2b54f0dbSXin Li 		case 12:
856*2b54f0dbSXin Li 			if (memcmp(line_start, "CPU revision", key_length) == 0) {
857*2b54f0dbSXin Li 				parse_cpu_revision(value_start, value_end, processor);
858*2b54f0dbSXin Li 			} else {
859*2b54f0dbSXin Li 				goto unknown;
860*2b54f0dbSXin Li 			}
861*2b54f0dbSXin Li 			break;
862*2b54f0dbSXin Li #if CPUINFO_ARCH_ARM
863*2b54f0dbSXin Li 		case 13:
864*2b54f0dbSXin Li 			if (memcmp(line_start, "I line length", key_length) == 0) {
865*2b54f0dbSXin Li 				parse_cache_number(value_start, value_end,
866*2b54f0dbSXin Li 					"instruction cache line size", &processor->proc_cpuinfo_cache.i_line_length,
867*2b54f0dbSXin Li 					&processor->flags, CPUINFO_ARM_LINUX_VALID_ICACHE_LINE);
868*2b54f0dbSXin Li 			} else if (memcmp(line_start, "D line length", key_length) == 0) {
869*2b54f0dbSXin Li 				parse_cache_number(value_start, value_end,
870*2b54f0dbSXin Li 					"data cache line size", &processor->proc_cpuinfo_cache.d_line_length,
871*2b54f0dbSXin Li 					&processor->flags, CPUINFO_ARM_LINUX_VALID_DCACHE_LINE);
872*2b54f0dbSXin Li 			} else {
873*2b54f0dbSXin Li 				goto unknown;
874*2b54f0dbSXin Li 			}
875*2b54f0dbSXin Li 			break;
876*2b54f0dbSXin Li #endif /* CPUINFO_ARCH_ARM */
877*2b54f0dbSXin Li 		case 15:
878*2b54f0dbSXin Li 			if (memcmp(line_start, "CPU implementer", key_length) == 0) {
879*2b54f0dbSXin Li 				parse_cpu_implementer(value_start, value_end, processor);
880*2b54f0dbSXin Li 			} else if (memcmp(line_start, "CPU implementor", key_length) == 0) {
881*2b54f0dbSXin Li 				parse_cpu_implementer(value_start, value_end, processor);
882*2b54f0dbSXin Li 			} else {
883*2b54f0dbSXin Li 				goto unknown;
884*2b54f0dbSXin Li 			}
885*2b54f0dbSXin Li 			break;
886*2b54f0dbSXin Li 		case 16:
887*2b54f0dbSXin Li 			if (memcmp(line_start, "CPU architecture", key_length) == 0) {
888*2b54f0dbSXin Li 				parse_cpu_architecture(value_start, value_end, processor);
889*2b54f0dbSXin Li 			} else {
890*2b54f0dbSXin Li 				goto unknown;
891*2b54f0dbSXin Li 			}
892*2b54f0dbSXin Li 			break;
893*2b54f0dbSXin Li 		default:
894*2b54f0dbSXin Li 		unknown:
895*2b54f0dbSXin Li 			cpuinfo_log_debug("unknown /proc/cpuinfo key: %.*s", (int) key_length, line_start);
896*2b54f0dbSXin Li 
897*2b54f0dbSXin Li 	}
898*2b54f0dbSXin Li 	return true;
899*2b54f0dbSXin Li }
900*2b54f0dbSXin Li 
cpuinfo_arm_linux_parse_proc_cpuinfo(char hardware[restrict static CPUINFO_HARDWARE_VALUE_MAX],char revision[restrict static CPUINFO_REVISION_VALUE_MAX],uint32_t max_processors_count,struct cpuinfo_arm_linux_processor processors[restrict static max_processors_count])901*2b54f0dbSXin Li bool cpuinfo_arm_linux_parse_proc_cpuinfo(
902*2b54f0dbSXin Li 	char hardware[restrict static CPUINFO_HARDWARE_VALUE_MAX],
903*2b54f0dbSXin Li 	char revision[restrict static CPUINFO_REVISION_VALUE_MAX],
904*2b54f0dbSXin Li 	uint32_t max_processors_count,
905*2b54f0dbSXin Li 	struct cpuinfo_arm_linux_processor processors[restrict static max_processors_count])
906*2b54f0dbSXin Li {
907*2b54f0dbSXin Li 	struct proc_cpuinfo_parser_state state = {
908*2b54f0dbSXin Li 		.hardware = hardware,
909*2b54f0dbSXin Li 		.revision = revision,
910*2b54f0dbSXin Li 		.processor_index = 0,
911*2b54f0dbSXin Li 		.max_processors_count = max_processors_count,
912*2b54f0dbSXin Li 		.processors = processors,
913*2b54f0dbSXin Li 	};
914*2b54f0dbSXin Li 	return cpuinfo_linux_parse_multiline_file("/proc/cpuinfo", BUFFER_SIZE,
915*2b54f0dbSXin Li 		(cpuinfo_line_callback) parse_line, &state);
916*2b54f0dbSXin Li }
917