• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <stdbool.h>
2 #include <stdint.h>
3 #include <stdio.h>
4 #include <string.h>
5 
6 #include <arm/linux/api.h>
7 #ifdef __ANDROID__
8 	#include <arm/android/api.h>
9 #endif
10 #include <cpuinfo/log.h>
11 #include <cpuinfo/common.h>
12 
13 
is_ascii_whitespace(char c)14 static inline bool is_ascii_whitespace(char c) {
15 	switch (c) {
16 		case ' ':
17 		case '\t':
18 		case '\r':
19 		case '\n':
20 			return true;
21 		default:
22 			return false;
23 	}
24 }
25 
is_ascii_alphabetic(char c)26 static inline bool is_ascii_alphabetic(char c) {
27 	const char lower_c = c | '\x20';
28 	return (uint8_t) (lower_c - 'a') <= (uint8_t) ('z' - 'a');
29 }
30 
is_ascii_alphabetic_uppercase(char c)31 static inline bool is_ascii_alphabetic_uppercase(char c) {
32 	return (uint8_t) (c - 'A') <= (uint8_t) ('Z' - 'A');
33 }
34 
is_ascii_numeric(char c)35 static inline bool is_ascii_numeric(char c) {
36 	return (uint8_t) (c - '0') < 10;
37 }
38 
load_u16le(const void * ptr)39 static inline uint16_t load_u16le(const void* ptr) {
40 #if defined(__ARM_ARCH_7A__) || defined(__aarch64__)
41     return *((const uint16_t*) ptr);
42 #else
43 	const uint8_t* byte_ptr = (const uint8_t*) ptr;
44 	return ((uint16_t) byte_ptr[1] << 8) | (uint16_t) byte_ptr[0];
45 #endif
46 }
47 
load_u24le(const void * ptr)48 static inline uint32_t load_u24le(const void* ptr) {
49 #if defined(__ARM_ARCH_7A__) || defined(__aarch64__)
50     return ((uint32_t) ((const uint8_t*) ptr)[2] << 16) | ((uint32_t) *((const uint16_t*) ptr));
51 #else
52 	const uint8_t* byte_ptr = (const uint8_t*) ptr;
53 	return ((uint32_t) byte_ptr[2] << 16) | ((uint32_t) byte_ptr[1] << 8) | (uint32_t) byte_ptr[0];
54 #endif
55 }
56 
load_u32le(const void * ptr)57 static inline uint32_t load_u32le(const void* ptr) {
58 #if defined(__ARM_ARCH_7A__) || defined(__aarch64__)
59     return *((const uint32_t*) ptr);
60 #else
61 	return ((uint32_t) ((const uint8_t*) ptr)[3] << 24) | load_u24le(ptr);
62 #endif
63 }
64 
65 /*
66  * Map from ARM chipset series ID to ARM chipset vendor ID.
67  * This map is used to avoid storing vendor IDs in tables.
68  */
69 static enum cpuinfo_arm_chipset_vendor chipset_series_vendor[cpuinfo_arm_chipset_series_max] = {
70 	[cpuinfo_arm_chipset_series_unknown]                = cpuinfo_arm_chipset_vendor_unknown,
71 	[cpuinfo_arm_chipset_series_qualcomm_qsd]           = cpuinfo_arm_chipset_vendor_qualcomm,
72 	[cpuinfo_arm_chipset_series_qualcomm_msm]           = cpuinfo_arm_chipset_vendor_qualcomm,
73 	[cpuinfo_arm_chipset_series_qualcomm_apq]           = cpuinfo_arm_chipset_vendor_qualcomm,
74 	[cpuinfo_arm_chipset_series_qualcomm_snapdragon]    = cpuinfo_arm_chipset_vendor_qualcomm,
75 	[cpuinfo_arm_chipset_series_mediatek_mt]            = cpuinfo_arm_chipset_vendor_mediatek,
76 	[cpuinfo_arm_chipset_series_samsung_exynos]         = cpuinfo_arm_chipset_vendor_samsung,
77 	[cpuinfo_arm_chipset_series_hisilicon_k3v]          = cpuinfo_arm_chipset_vendor_hisilicon,
78 	[cpuinfo_arm_chipset_series_hisilicon_hi]           = cpuinfo_arm_chipset_vendor_hisilicon,
79 	[cpuinfo_arm_chipset_series_hisilicon_kirin]        = cpuinfo_arm_chipset_vendor_hisilicon,
80 	[cpuinfo_arm_chipset_series_actions_atm]            = cpuinfo_arm_chipset_vendor_actions,
81 	[cpuinfo_arm_chipset_series_allwinner_a]            = cpuinfo_arm_chipset_vendor_allwinner,
82 	[cpuinfo_arm_chipset_series_amlogic_aml]            = cpuinfo_arm_chipset_vendor_amlogic,
83 	[cpuinfo_arm_chipset_series_amlogic_s]              = cpuinfo_arm_chipset_vendor_amlogic,
84 	[cpuinfo_arm_chipset_series_broadcom_bcm]           = cpuinfo_arm_chipset_vendor_broadcom,
85 	[cpuinfo_arm_chipset_series_lg_nuclun]              = cpuinfo_arm_chipset_vendor_lg,
86 	[cpuinfo_arm_chipset_series_leadcore_lc]            = cpuinfo_arm_chipset_vendor_leadcore,
87 	[cpuinfo_arm_chipset_series_marvell_pxa]            = cpuinfo_arm_chipset_vendor_marvell,
88 	[cpuinfo_arm_chipset_series_mstar_6a]               = cpuinfo_arm_chipset_vendor_mstar,
89 	[cpuinfo_arm_chipset_series_novathor_u]             = cpuinfo_arm_chipset_vendor_novathor,
90 	[cpuinfo_arm_chipset_series_nvidia_tegra_t]         = cpuinfo_arm_chipset_vendor_nvidia,
91 	[cpuinfo_arm_chipset_series_nvidia_tegra_ap]        = cpuinfo_arm_chipset_vendor_nvidia,
92 	[cpuinfo_arm_chipset_series_nvidia_tegra_sl]        = cpuinfo_arm_chipset_vendor_nvidia,
93 	[cpuinfo_arm_chipset_series_pinecone_surge_s]       = cpuinfo_arm_chipset_vendor_pinecone,
94 	[cpuinfo_arm_chipset_series_renesas_mp]             = cpuinfo_arm_chipset_vendor_renesas,
95 	[cpuinfo_arm_chipset_series_rockchip_rk]            = cpuinfo_arm_chipset_vendor_rockchip,
96 	[cpuinfo_arm_chipset_series_spreadtrum_sc]          = cpuinfo_arm_chipset_vendor_spreadtrum,
97 	[cpuinfo_arm_chipset_series_telechips_tcc]          = cpuinfo_arm_chipset_vendor_telechips,
98 	[cpuinfo_arm_chipset_series_texas_instruments_omap] = cpuinfo_arm_chipset_vendor_texas_instruments,
99 	[cpuinfo_arm_chipset_series_wondermedia_wm]         = cpuinfo_arm_chipset_vendor_wondermedia,
100 };
101 
102 /**
103  * Tries to match /(MSM|APQ)\d{4}([A-Z\-]*)/ signature (case-insensitive) for Qualcomm MSM and APQ chipsets.
104  * If match successful, extracts model information into \p chipset argument.
105  *
106  * @param start - start of the platform identifier (/proc/cpuinfo Hardware string, ro.product.board, ro.board.platform
107  *                or ro.chipname) to match.
108  * @param end - end of the platform identifier (/proc/cpuinfo Hardware string, ro.product.board, ro.board.platform or
109  *              ro.chipname) to match.
110  * @param[out] chipset - location where chipset information will be stored upon a successful match.
111  *
112  * @returns true if signature matched, false otherwise.
113  */
match_msm_apq(const char * start,const char * end,struct cpuinfo_arm_chipset chipset[restrict static1])114 static bool match_msm_apq(
115 	const char* start, const char* end,
116 	struct cpuinfo_arm_chipset chipset[restrict static 1])
117 {
118 	/* Expect at least 7 symbols: 3 symbols "MSM" or "APQ" + 4 digits */
119 	if (start + 7 > end) {
120 		return false;
121 	}
122 
123 	/* Check that string starts with "MSM" or "APQ", case-insensitive.
124 	 * The first three characters are loaded as 24-bit little endian word, binary ORed with 0x20 to convert to lower
125 	 * case, and compared to "MSM" and "APQ" strings as integers.
126 	 */
127 	const uint32_t series_signature = UINT32_C(0x00202020) | load_u24le(start);
128 	enum cpuinfo_arm_chipset_series series;
129 	switch (series_signature) {
130 		case UINT32_C(0x6D736D): /* "msm" = reverse("msm") */
131 			series = cpuinfo_arm_chipset_series_qualcomm_msm;
132 			break;
133 		case UINT32_C(0x717061): /* "qpa" = reverse("apq") */
134 			series = cpuinfo_arm_chipset_series_qualcomm_apq;
135 			break;
136 		default:
137 			return false;
138 	}
139 
140 	/* Sometimes there is a space ' ' following the MSM/APQ series */
141 	const char* pos = start + 3;
142 	if (*pos == ' ') {
143 		pos++;
144 
145 		/* Expect at least 4 more symbols (4-digit model number) */
146 		if (pos + 4 > end) {
147 			return false;
148 		}
149 	}
150 
151 	/* Validate and parse 4-digit model number */
152 	uint32_t model = 0;
153 	for (uint32_t i = 0; i < 4; i++) {
154 		const uint32_t digit = (uint32_t) (uint8_t) (*pos++) - '0';
155 		if (digit >= 10) {
156 			/* Not really a digit */
157 			return false;
158 		}
159 		model = model * 10 + digit;
160 	}
161 
162 	/* Suffix is optional, so if we got to this point, parsing is successful. Commit parsed chipset. */
163 	*chipset = (struct cpuinfo_arm_chipset) {
164 		.vendor = cpuinfo_arm_chipset_vendor_qualcomm,
165 		.series = series,
166 		.model = model,
167 	};
168 
169 	/* Parse as many suffix characters as match the pattern [A-Za-z\-] */
170 	for (uint32_t i = 0; i < CPUINFO_ARM_CHIPSET_SUFFIX_MAX; i++) {
171 		if (pos + i == end) {
172 			break;
173 		}
174 
175 		const char c = pos[i];
176 		if (is_ascii_alphabetic(c)) {
177 			/* Matched a letter [A-Za-z] */
178 			chipset->suffix[i] = c & '\xDF';
179 		} else if (c == '-') {
180 			/* Matched a dash '-' */
181 			chipset->suffix[i] = c;
182 		} else {
183 			/* Neither of [A-Za-z\-] */
184 			break;
185 		}
186 	}
187 	return true;
188 }
189 
190 /**
191  * Tries to match /SDM\d{3}$/ signature for Qualcomm Snapdragon chipsets.
192  * If match successful, extracts model information into \p chipset argument.
193  *
194  * @param start - start of the /proc/cpuinfo Hardware string to match.
195  * @param end - end of the /proc/cpuinfo Hardware string to match.
196  * @param[out] chipset - location where chipset information will be stored upon a successful match.
197  *
198  * @returns true if signature matched, false otherwise.
199  */
match_sdm(const char * start,const char * end,struct cpuinfo_arm_chipset chipset[restrict static1])200 static bool match_sdm(
201 	const char* start, const char* end,
202 	struct cpuinfo_arm_chipset chipset[restrict static 1])
203 {
204 	/* Expect exactly 6 symbols: 3 symbols "SDM" + 3 digits */
205 	if (start + 6 != end) {
206 		return false;
207 	}
208 
209 	/* Check that string starts with "SDM".
210 	 * The first three characters are loaded and compared as 24-bit little endian word.
211 	 */
212 	const uint32_t expected_sdm = load_u24le(start);
213 	if (expected_sdm != UINT32_C(0x004D4453) /* "MDS" = reverse("SDM") */) {
214 		return false;
215 	}
216 
217 	/* Validate and parse 3-digit model number */
218 	uint32_t model = 0;
219 	for (uint32_t i = 3; i < 6; i++) {
220 		const uint32_t digit = (uint32_t) (uint8_t) start[i] - '0';
221 		if (digit >= 10) {
222 			/* Not really a digit */
223 			return false;
224 		}
225 		model = model * 10 + digit;
226 	}
227 
228 	/* Return parsed chipset. */
229 	*chipset = (struct cpuinfo_arm_chipset) {
230 		.vendor = cpuinfo_arm_chipset_vendor_qualcomm,
231 		.series = cpuinfo_arm_chipset_series_qualcomm_snapdragon,
232 		.model = model,
233 	};
234 	return true;
235 }
236 
237 /**
238  * Tries to match /SM\d{4}$/ signature for Qualcomm Snapdragon chipsets.
239  * If match successful, extracts model information into \p chipset argument.
240  *
241  * @param start - start of the /proc/cpuinfo Hardware string to match.
242  * @param end - end of the /proc/cpuinfo Hardware string to match.
243  * @param[out] chipset - location where chipset information will be stored upon a successful match.
244  *
245  * @returns true if signature matched, false otherwise.
246  */
match_sm(const char * start,const char * end,struct cpuinfo_arm_chipset chipset[restrict static1])247 static bool match_sm(
248 	const char* start, const char* end,
249 	struct cpuinfo_arm_chipset chipset[restrict static 1])
250 {
251 	/* Expect exactly 6 symbols: 2 symbols "SM" + 4 digits */
252 	if (start + 6 != end) {
253 		return false;
254 	}
255 
256 	/* Check that string starts with "SM".
257 	 * The first three characters are loaded and compared as 16-bit little endian word.
258 	 */
259 	const uint32_t expected_sm = load_u16le(start);
260 	if (expected_sm != UINT16_C(0x4D53) /* "MS" = reverse("SM") */) {
261 		return false;
262 	}
263 
264 	/* Validate and parse 4-digit model number */
265 	uint32_t model = 0;
266 	for (uint32_t i = 2; i < 6; i++) {
267 		const uint32_t digit = (uint32_t) (uint8_t) start[i] - '0';
268 		if (digit >= 10) {
269 			/* Not really a digit */
270 			return false;
271 		}
272 		model = model * 10 + digit;
273 	}
274 
275 	/* Return parsed chipset. */
276 	*chipset = (struct cpuinfo_arm_chipset) {
277 		.vendor = cpuinfo_arm_chipset_vendor_qualcomm,
278 		.series = cpuinfo_arm_chipset_series_qualcomm_snapdragon,
279 		.model = model,
280 	};
281 	return true;
282 }
283 
284 /**
285  * Tries to match /Samsung Exynos\d{4}$/ signature (case-insensitive) for Samsung Exynos chipsets.
286  * If match successful, extracts model information into \p chipset argument.
287  *
288  * @param start - start of the /proc/cpuinfo Hardware string to match.
289  * @param end - end of the /proc/cpuinfo Hardware string to match.
290  * @param[out] chipset - location where chipset information will be stored upon a successful match.
291  *
292  * @returns true if signature matched, false otherwise.
293  */
match_samsung_exynos(const char * start,const char * end,struct cpuinfo_arm_chipset chipset[restrict static1])294 static bool match_samsung_exynos(
295 	const char* start, const char* end,
296 	struct cpuinfo_arm_chipset chipset[restrict static 1])
297 {
298 	/*
299 	 * Expect at 18-19 symbols:
300 	 * - "Samsung" (7 symbols) + space + "Exynos" (6 symbols) + optional space 4-digit model number
301 	 */
302 	const size_t length = end - start;
303 	switch (length) {
304 		case 18:
305 		case 19:
306 			break;
307 		default:
308 			return false;
309 	}
310 
311 	/*
312 	 * Check that the string starts with "samsung exynos", case-insensitive.
313 	 * Blocks of 4 characters are loaded and compared as little-endian 32-bit word.
314 	 * Case-insensitive characters are binary ORed with 0x20 to convert them to lowercase.
315 	 */
316 	const uint32_t expected_sams = UINT32_C(0x20202000) | load_u32le(start);
317 	if (expected_sams != UINT32_C(0x736D6153) /* "smaS" = reverse("Sams") */) {
318 		return false;
319 	}
320 	const uint32_t expected_ung = UINT32_C(0x00202020) | load_u32le(start + 4);
321 	if (expected_ung != UINT32_C(0x20676E75) /* " ung" = reverse("ung ") */) {
322 		return false;
323 	}
324 	const uint32_t expected_exyn = UINT32_C(0x20202000) | load_u32le(start + 8);
325 	if (expected_exyn != UINT32_C(0x6E797845) /* "nyxE" = reverse("Exyn") */) {
326 		return false;
327 	}
328 	const uint16_t expected_os = UINT16_C(0x2020) | load_u16le(start + 12);
329 	if (expected_os != UINT16_C(0x736F) /* "so" = reverse("os") */) {
330 		return false;
331 	}
332 
333 	const char* pos = start + 14;
334 
335 	/* There can be a space ' ' following the "Exynos" string */
336 	if (*pos == ' ') {
337 		pos++;
338 
339 		/* If optional space if present, we expect exactly 19 characters */
340 		if (length != 19) {
341 			return false;
342 		}
343 	}
344 
345 	/* Validate and parse 4-digit model number */
346 	uint32_t model = 0;
347 	for (uint32_t i = 0; i < 4; i++) {
348 		const uint32_t digit = (uint32_t) (uint8_t) (*pos++) - '0';
349 		if (digit >= 10) {
350 			/* Not really a digit */
351 			return false;
352 		}
353 		model = model * 10 + digit;
354 	}
355 
356 	/* Return parsed chipset */
357 	*chipset = (struct cpuinfo_arm_chipset) {
358 		.vendor = cpuinfo_arm_chipset_vendor_samsung,
359 		.series = cpuinfo_arm_chipset_series_samsung_exynos,
360 		.model = model,
361 	};
362 	return true;
363 }
364 
365 /**
366  * Tries to match /exynos\d{4}$/ signature for Samsung Exynos chipsets.
367  * If match successful, extracts model information into \p chipset argument.
368  *
369  * @param start - start of the platform identifier (ro.board.platform or ro.chipname) to match.
370  * @param end - end of the platform identifier (ro.board.platform or ro.chipname) to match.
371  * @param[out] chipset - location where chipset information will be stored upon a successful match.
372  *
373  * @returns true if signature matched, false otherwise.
374  */
match_exynos(const char * start,const char * end,struct cpuinfo_arm_chipset chipset[restrict static1])375 static bool match_exynos(
376 	const char* start, const char* end,
377 	struct cpuinfo_arm_chipset chipset[restrict static 1])
378 {
379 	/* Expect exactly 10 symbols: "exynos" (6 symbols) + 4-digit model number */
380 	if (start + 10 != end) {
381 		return false;
382 	}
383 
384 	/* Load first 4 bytes as little endian 32-bit word */
385 	const uint32_t expected_exyn = load_u32le(start);
386 	if (expected_exyn != UINT32_C(0x6E797865) /* "nyxe" = reverse("exyn") */ ) {
387 		return false;
388 	}
389 
390 	/* Load next 2 bytes as little endian 16-bit word */
391 	const uint16_t expected_os = load_u16le(start + 4);
392 	if (expected_os != UINT16_C(0x736F) /* "so" = reverse("os") */ ) {
393 		return false;
394 	}
395 
396 	/* Check and parse 4-digit model number */
397 	uint32_t model = 0;
398 	for (uint32_t i = 6; i < 10; i++) {
399 		const uint32_t digit = (uint32_t) (uint8_t) start[i] - '0';
400 		if (digit >= 10) {
401 			/* Not really a digit */
402 			return false;
403 		}
404 		model = model * 10 + digit;
405 	}
406 
407 	/* Return parsed chipset. */
408 	*chipset = (struct cpuinfo_arm_chipset) {
409 		.vendor = cpuinfo_arm_chipset_vendor_samsung,
410 		.series = cpuinfo_arm_chipset_series_samsung_exynos,
411 		.model = model,
412 	};
413 	return true;
414 }
415 
416 /**
417  * Tries to match /universal\d{4}$/ signature for Samsung Exynos chipsets.
418  * If match successful, extracts model information into \p chipset argument.
419  *
420  * @param start - start of the platform identifier (/proc/cpuinfo Hardware string, ro.product.board or ro.chipname)
421  *                to match.
422  * @param end - end of the platform identifier (/proc/cpuinfo Hardware string, ro.product.board or ro.chipname)
423  *              to match.
424  * @param[out] chipset - location where chipset information will be stored upon a successful match.
425  *
426  * @returns true if signature matched, false otherwise.
427  */
match_universal(const char * start,const char * end,struct cpuinfo_arm_chipset chipset[restrict static1])428 static bool match_universal(
429 	const char* start, const char* end,
430 	struct cpuinfo_arm_chipset chipset[restrict static 1])
431 {
432 	/* Expect exactly 13 symbols: "universal" (9 symbols) + 4-digit model number */
433 	if (start + 13 != end) {
434 		return false;
435 	}
436 
437 	/*
438 	 * Check that the string starts with "universal".
439 	 * Blocks of 4 characters are loaded and compared as little-endian 32-bit word.
440 	 * Case-insensitive characters are binary ORed with 0x20 to convert them to lowercase.
441 	 */
442 	const uint8_t expected_u = UINT8_C(0x20) | (uint8_t) start[0];
443 	if (expected_u != UINT8_C(0x75) /* "u" */) {
444 		return false;
445 	}
446 	const uint32_t expected_nive = UINT32_C(0x20202020) | load_u32le(start + 1);
447 	if (expected_nive != UINT32_C(0x6576696E) /* "evin" = reverse("nive") */ ) {
448 		return false;
449 	}
450 	const uint32_t expected_ersa = UINT32_C(0x20202020) | load_u32le(start + 5);
451 	if (expected_ersa != UINT32_C(0x6C617372) /* "lasr" = reverse("rsal") */) {
452 		return false;
453 	}
454 
455 	/* Validate and parse 4-digit model number */
456 	uint32_t model = 0;
457 	for (uint32_t i = 9; i < 13; i++) {
458 		const uint32_t digit = (uint32_t) (uint8_t) start[i] - '0';
459 		if (digit >= 10) {
460 			/* Not really a digit */
461 			return false;
462 		}
463 		model = model * 10 + digit;
464 	}
465 
466 	/* Return parsed chipset. */
467 	*chipset = (struct cpuinfo_arm_chipset) {
468 		.vendor = cpuinfo_arm_chipset_vendor_samsung,
469 		.series = cpuinfo_arm_chipset_series_samsung_exynos,
470 		.model = model,
471 	};
472 	return true;
473 }
474 
475 /**
476  * Compares, case insensitively, a string to known values "SMDK4210" and "SMDK4x12" for Samsung Exynos chipsets.
477  * If platform identifier matches one of the SMDK* values, extracts model information into \p chipset argument.
478  * For "SMDK4x12" match, decodes the chipset name using number of cores.
479  *
480  * @param start - start of the platform identifier (/proc/cpuinfo Hardware string or ro.product.board) to match.
481  * @param end - end of the platform identifier (/proc/cpuinfo Hardware string or ro.product.board) to match.
482  * @param cores - number of cores in the chipset.
483  * @param[out] chipset - location where chipset information will be stored upon a successful match.
484  *
485  * @returns true if signature matched, false otherwise.
486  */
match_and_parse_smdk(const char * start,const char * end,uint32_t cores,struct cpuinfo_arm_chipset chipset[restrict static1])487 static bool match_and_parse_smdk(
488 	const char* start, const char* end, uint32_t cores,
489 	struct cpuinfo_arm_chipset chipset[restrict static 1])
490 {
491 	/* Expect exactly 8 symbols: "SMDK" (4 symbols) + 4-digit model number */
492 	if (start + 8 != end) {
493 		return false;
494 	}
495 
496 	/*
497 	 * Check that string starts with "MT" (case-insensitive).
498 	 * The first four characters are loaded as a 32-bit little endian word and converted to lowercase.
499 	 */
500 	const uint32_t expected_smdk = UINT32_C(0x20202020) | load_u32le(start);
501 	if (expected_smdk != UINT32_C(0x6B646D73) /* "kdms" = reverse("smdk") */) {
502 		return false;
503 	}
504 
505 	/*
506 	 * Check that string ends with "4210" or "4x12".
507 	 * The last four characters are loaded and compared as a 32-bit little endian word.
508 	 */
509 	uint32_t model = 0;
510 	const uint32_t expected_model = load_u32le(start + 4);
511 	switch (expected_model) {
512 		case UINT32_C(0x30313234): /* "0124" = reverse("4210") */
513 			model = 4210;
514 			break;
515 		case UINT32_C(0x32317834): /* "21x4" = reverse("4x12") */
516 			switch (cores) {
517 				case 2:
518 					model = 4212;
519 					break;
520 				case 4:
521 					model = 4412;
522 					break;
523 				default:
524 					cpuinfo_log_warning("system reported invalid %"PRIu32"-core Exynos 4x12 chipset", cores);
525 			}
526 	}
527 
528 	if (model == 0) {
529 		return false;
530 	}
531 
532 	*chipset = (struct cpuinfo_arm_chipset) {
533 		.vendor = cpuinfo_arm_chipset_vendor_samsung,
534 		.series = cpuinfo_arm_chipset_series_samsung_exynos,
535 		.model = model,
536 	};
537 	return true;
538 }
539 
540 /**
541  * Tries to match /MTK?\d{4}[A-Z/]*$/ signature for MediaTek MT chipsets.
542  * If match successful, extracts model information into \p chipset argument.
543  *
544  * @param start - start of the platform identifier (/proc/cpuinfo Hardware string, ro.product.board, ro.board.platform,
545  *                ro.mediatek.platform, or ro.chipname) to match.
546  * @param end - end of the platform identifier (/proc/cpuinfo Hardware string, ro.product.board, ro.board.platform,
547  *              ro.mediatek.platform, or ro.chipname) to match.
548  * @param match_end - indicates if the function should attempt to match through the end of the string and fail if there
549  *                    are unparsed characters in the end, or match only MTK signature, model number, and some of the
550  *                    suffix characters (the ones that pass validation).
551  * @param[out] chipset - location where chipset information will be stored upon a successful match.
552  *
553  * @returns true if signature matched, false otherwise.
554  */
match_mt(const char * start,const char * end,bool match_end,struct cpuinfo_arm_chipset chipset[restrict static1])555 static bool match_mt(
556 	const char* start, const char* end, bool match_end,
557 	struct cpuinfo_arm_chipset chipset[restrict static 1])
558 {
559 	/* Expect at least 6 symbols: "MT" (2 symbols) + 4-digit model number */
560 	if (start + 6 > end) {
561 		return false;
562 	}
563 
564 	/*
565 	 * Check that string starts with "MT" (case-insensitive).
566 	 * The first two characters are loaded as 16-bit little endian word and converted to lowercase.
567 	 */
568 	const uint16_t mt = UINT16_C(0x2020) | load_u16le(start);
569 	if (mt != UINT16_C(0x746D) /* "tm" */) {
570 		return false;
571 	}
572 
573 
574 	/* Some images report "MTK" rather than "MT" */
575 	const char* pos = start + 2;
576 	if (((uint8_t) *pos | UINT8_C(0x20)) == (uint8_t) 'k') {
577 		pos++;
578 
579 		/* Expect 4 more symbols after "MTK" (4-digit model number) */
580 		if (pos + 4 > end) {
581 			return false;
582 		}
583 	}
584 
585 	/* Validate and parse 4-digit model number */
586 	uint32_t model = 0;
587 	for (uint32_t i = 0; i < 4; i++) {
588 		const uint32_t digit = (uint32_t) (uint8_t) (*pos++) - '0';
589 		if (digit >= 10) {
590 			/* Not really a digit */
591 			return false;
592 		}
593 		model = model * 10 + digit;
594 	}
595 
596 	/* Record parsed chipset. This implicitly zeroes-out suffix, which will be parsed later. */
597 	*chipset = (struct cpuinfo_arm_chipset) {
598 		.vendor = cpuinfo_arm_chipset_vendor_mediatek,
599 		.series = cpuinfo_arm_chipset_series_mediatek_mt,
600 		.model = model,
601 	};
602 
603 	if (match_end) {
604 		/* Check that the potential suffix does not exceed maximum length */
605 		const size_t suffix_length = end - pos;
606 		if (suffix_length > CPUINFO_ARM_CHIPSET_SUFFIX_MAX) {
607 			return false;
608 		}
609 
610 		/* Validate suffix characters and copy them to chipset structure */
611 		for (size_t i = 0; i < suffix_length; i++) {
612 			const char c = (*pos++);
613 			if (is_ascii_alphabetic(c)) {
614 				/* Matched a letter [A-Za-z], convert to uppercase */
615 				chipset->suffix[i] = c & '\xDF';
616 			} else if (c == '/') {
617 				/* Matched a slash '/' */
618 				chipset->suffix[i] = c;
619 			} else {
620 				/* Invalid suffix character (neither of [A-Za-z/]) */
621 				return false;
622 			}
623 		}
624 	} else {
625 		/* Validate and parse as many suffix characters as we can */
626 		for (size_t i = 0; i < CPUINFO_ARM_CHIPSET_SUFFIX_MAX; i++) {
627 			if (pos + i == end) {
628 				break;
629 			}
630 
631 			const char c = pos[i];
632 			if (is_ascii_alphabetic(c)) {
633 				/* Matched a letter [A-Za-z], convert to uppercase */
634 				chipset->suffix[i] = c & '\xDF';
635 			} else if (c == '/') {
636 				/* Matched a slash '/' */
637 				chipset->suffix[i] = c;
638 			} else {
639 				/* Invalid suffix character (neither of [A-Za-z/]). This marks the end of the suffix. */
640 				break;
641 			}
642 		}
643 	}
644 	/* All suffix characters successfully validated and copied to chipset data */
645 	return true;
646 }
647 
648 /**
649  * Tries to match /[Kk]irin\s?\d{3}$/ signature for HiSilicon Kirin chipsets.
650  * If match successful, extracts model information into \p chipset argument.
651  *
652  * @param start - start of the /proc/cpuinfo Hardware string to match.
653  * @param end - end of the /proc/cpuinfo Hardware string to match.
654  * @param[out] chipset - location where chipset information will be stored upon a successful match.
655  *
656  * @returns true if signature matched, false otherwise.
657  */
match_kirin(const char * start,const char * end,struct cpuinfo_arm_chipset chipset[restrict static1])658 static bool match_kirin(
659 	const char* start, const char* end,
660 	struct cpuinfo_arm_chipset chipset[restrict static 1])
661 {
662 	/* Expect 8-9 symbols: "Kirin" (5 symbols) + optional whitespace (1 symbol) + 3-digit model number */
663 	const size_t length = end - start;
664 	switch (length) {
665 		case 8:
666 		case 9:
667 			break;
668 		default:
669 			return false;
670 	}
671 
672 	/* Check that the string starts with "Kirin" or "kirin". */
673 	if (((uint8_t) start[0] | UINT8_C(0x20)) != (uint8_t) 'k') {
674 		return false;
675 	}
676 	/* Symbols 1-5 are loaded and compared as little-endian 32-bit word. */
677 	const uint32_t irin = load_u32le(start + 1);
678 	if (irin != UINT32_C(0x6E697269) /* "niri" = reverse("irin") */) {
679 		return false;
680 	}
681 
682 	/* Check for optional whitespace after "Kirin" */
683 	if (is_ascii_whitespace(start[5])) {
684 		/* When whitespace is present after "Kirin", expect 9 symbols total */
685 		if (length != 9) {
686 			return false;
687 		}
688 	}
689 
690 	/* Validate and parse 3-digit model number */
691 	uint32_t model = 0;
692 	for (int32_t i = 0; i < 3; i++) {
693 		const uint32_t digit = (uint32_t) (uint8_t) end[i - 3] - '0';
694 		if (digit >= 10) {
695 			/* Not really a digit */
696 			return false;
697 		}
698 		model = model * 10 + digit;
699 	}
700 
701 	/*
702 	 * Thats it, return parsed chipset.
703 	 * Technically, Kirin 910T has a suffix, but it never appears in the form of "910T" string.
704 	 * Instead, Kirin 910T devices report "hi6620oem" string (handled outside of this function).
705 	 */
706 	*chipset = (struct cpuinfo_arm_chipset) {
707 		.vendor = cpuinfo_arm_chipset_vendor_hisilicon,
708 		.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
709 		.model = model,
710 	};
711 	return true;
712 }
713 
714 /**
715  * Tries to match /rk\d{4}[a-z]?$/ signature for Rockchip RK chipsets.
716  * If match successful, extracts model information into \p chipset argument.
717  *
718  * @param start - start of the platform identifier (/proc/cpuinfo Hardware string or ro.board.platform) to match.
719  * @param end - end of the platform identifier (/proc/cpuinfo Hardware string or ro.board.platform) to match.
720  * @param[out] chipset - location where chipset information will be stored upon a successful match.
721  *
722  * @returns true if signature matched, false otherwise.
723  */
match_rk(const char * start,const char * end,struct cpuinfo_arm_chipset chipset[restrict static1])724 static bool match_rk(
725 	const char* start, const char* end,
726 	struct cpuinfo_arm_chipset chipset[restrict static 1])
727 {
728 	/* Expect 6-7 symbols: "RK" (2 symbols) + 4-digit model number + optional 1-letter suffix */
729 	const size_t length = end - start;
730 	switch (length) {
731 		case 6:
732 		case 7:
733 			break;
734 		default:
735 			return false;
736 	}
737 
738 	/*
739 	 * Check that string starts with "RK" (case-insensitive).
740 	 * The first two characters are loaded as 16-bit little endian word and converted to lowercase.
741 	 */
742 	const uint16_t expected_rk = UINT16_C(0x2020) | load_u16le(start);
743 	if (expected_rk != UINT16_C(0x6B72) /* "kr" = reverse("rk") */) {
744 		return false;
745 	}
746 
747 	/* Validate and parse 4-digit model number */
748 	uint32_t model = 0;
749 	for (uint32_t i = 2; i < 6; i++) {
750 		const uint32_t digit = (uint32_t) (uint8_t) start[i] - '0';
751 		if (digit >= 10) {
752 			/* Not really a digit */
753 			return false;
754 		}
755 		model = model * 10 + digit;
756 	}
757 
758 	/* Parse optional suffix */
759 	char suffix = 0;
760 	if (length == 7) {
761 		/* Parse the suffix letter */
762 		const char c = start[6];
763 		if (is_ascii_alphabetic(c)) {
764 			/* Convert to upper case */
765 			suffix = c & '\xDF';
766 		} else {
767 			/* Invalid suffix character */
768 			return false;
769 		}
770 	}
771 
772 	/* Return parsed chipset */
773 	*chipset = (struct cpuinfo_arm_chipset) {
774 		.vendor = cpuinfo_arm_chipset_vendor_rockchip,
775 		.series = cpuinfo_arm_chipset_series_rockchip_rk,
776 		.model = model,
777 		.suffix = {
778 			[0] = suffix,
779 		},
780 	};
781 	return true;
782 }
783 
784 /**
785  * Tries to match, case-insentitively, /s[cp]\d{4}[a-z]*|scx15$/ signature for Spreadtrum SC chipsets.
786  * If match successful, extracts model information into \p chipset argument.
787  *
788  * @param start - start of the platform identifier (/proc/cpuinfo Hardware string, ro.product.board,
789  *                ro.board.platform, or ro.chipname) to match.
790  * @param end - end of the platform identifier (/proc/cpuinfo Hardware string, ro.product.board,
791  *              ro.board.platform, or ro.chipname) to match.
792  * @param[out] chipset - location where chipset information will be stored upon a successful match.
793  *
794  * @returns true if signature matched, false otherwise.
795  */
match_sc(const char * start,const char * end,struct cpuinfo_arm_chipset chipset[restrict static1])796 static bool match_sc(
797 	const char* start, const char* end,
798 	struct cpuinfo_arm_chipset chipset[restrict static 1])
799 {
800 	/* Expect at least 5 symbols: "scx15" */
801 	if (start + 5 > end) {
802 		return false;
803 	}
804 
805 	/*
806 	 * Check that string starts with "S[CP]" (case-insensitive).
807 	 * The first two characters are loaded as 16-bit little endian word and converted to lowercase.
808 	 */
809 	const uint16_t expected_sc_or_sp = UINT16_C(0x2020) | load_u16le(start);
810 	switch (expected_sc_or_sp) {
811 		case UINT16_C(0x6373): /* "cs" = reverse("sc") */
812 		case UINT16_C(0x7073): /* "ps" = reverse("sp") */
813 			break;
814 		default:
815 			return false;
816 	}
817 
818 	/* Special case: "scx" prefix (SC7715 reported as "scx15") */
819 	if ((start[2] | '\x20') == 'x') {
820 		/* Expect exactly 5 characters: "scx15" */
821 		if (start + 5 != end) {
822 			return false;
823 		}
824 
825 		/* Check that string ends with "15" */
826 		const uint16_t expected_15 = load_u16le(start + 3);
827 		if (expected_15 != UINT16_C(0x3531) /* "51" = reverse("15") */ ) {
828 			return false;
829 		}
830 
831 		*chipset = (struct cpuinfo_arm_chipset) {
832 			.vendor = cpuinfo_arm_chipset_vendor_spreadtrum,
833 			.series = cpuinfo_arm_chipset_series_spreadtrum_sc,
834 			.model = 7715,
835 		};
836 		return true;
837 	}
838 
839 	/* Expect at least 6 symbols: "S[CP]" (2 symbols) + 4-digit model number */
840 	if (start + 6 > end) {
841 		return false;
842 	}
843 
844 	/* Validate and parse 4-digit model number */
845 	uint32_t model = 0;
846 	for (uint32_t i = 2; i < 6; i++) {
847 		const uint32_t digit = (uint32_t) (uint8_t) start[i] - '0';
848 		if (digit >= 10) {
849 			/* Not really a digit */
850 			return false;
851 		}
852 		model = model * 10 + digit;
853 	}
854 
855 	/* Write parsed chipset */
856 	*chipset = (struct cpuinfo_arm_chipset) {
857 		.vendor = cpuinfo_arm_chipset_vendor_spreadtrum,
858 		.series = cpuinfo_arm_chipset_series_spreadtrum_sc,
859 		.model = model,
860 	};
861 
862 	/* Validate and copy suffix letters. If suffix is too long, truncate at CPUINFO_ARM_CHIPSET_SUFFIX_MAX letters. */
863 	const char* suffix = start + 6;
864 	for (size_t i = 0; i < CPUINFO_ARM_CHIPSET_SUFFIX_MAX; i++) {
865 		if (suffix + i == end) {
866 			break;
867 		}
868 
869 		const char c = suffix[i];
870 		if (!is_ascii_alphabetic(c)) {
871 			/* Invalid suffix character */
872 			return false;
873 		}
874 		/* Convert suffix letter to uppercase */
875 		chipset->suffix[i] = c & '\xDF';
876 	}
877 	return true;
878 }
879 
880 /**
881  * Tries to match /lc\d{4}[a-z]?$/ signature for Leadcore LC chipsets.
882  * If match successful, extracts model information into \p chipset argument.
883  *
884  * @param start - start of the platform identifier (ro.product.board or ro.board.platform) to match.
885  * @param end - end of the platform identifier (ro.product.board or ro.board.platform) to match.
886  * @param[out] chipset - location where chipset information will be stored upon a successful match.
887  *
888  * @returns true if signature matched, false otherwise.
889  */
match_lc(const char * start,const char * end,struct cpuinfo_arm_chipset chipset[restrict static1])890 static bool match_lc(
891 	const char* start, const char* end,
892 	struct cpuinfo_arm_chipset chipset[restrict static 1])
893 {
894 	/* Expect at 6-7 symbols: "lc" (2 symbols) + 4-digit model number + optional 1-letter suffix */
895 	const size_t length = end - start;
896 	switch (length) {
897 		case 6:
898 		case 7:
899 			break;
900 		default:
901 			return false;
902 	}
903 
904 	/* Check that string starts with "lc". The first two characters are loaded as 16-bit little endian word */
905 	const uint16_t expected_lc = load_u16le(start);
906 	if (expected_lc != UINT16_C(0x636C) /* "cl" = reverse("lc") */) {
907 		return false;
908 	}
909 
910 	/* Validate and parse 4-digit model number */
911 	uint32_t model = 0;
912 	for (uint32_t i = 2; i < 6; i++) {
913 		const uint32_t digit = (uint32_t) (uint8_t) start[i] - '0';
914 		if (digit >= 10) {
915 			/* Not really a digit */
916 			return false;
917 		}
918 		model = model * 10 + digit;
919 	}
920 
921 	/* Parse optional suffix letter */
922 	char suffix = 0;
923 	if (length == 7) {
924 		const char c = start[6];
925 		if (is_ascii_alphabetic(c)) {
926 			/* Convert to uppercase */
927 			chipset->suffix[0] = c & '\xDF';
928 		} else {
929 			/* Invalid suffix character */
930 			return false;
931 		}
932 	}
933 
934 	/* Return parsed chipset */
935 	*chipset = (struct cpuinfo_arm_chipset) {
936 		.vendor = cpuinfo_arm_chipset_vendor_leadcore,
937 		.series = cpuinfo_arm_chipset_series_leadcore_lc,
938 		.model = model,
939 		.suffix = {
940 			[0] = suffix,
941 		},
942 	};
943 	return true;
944 }
945 
946 /**
947  * Tries to match /PXA(\d{3,4}|1L88)$/ signature for Marvell PXA chipsets.
948  * If match successful, extracts model information into \p chipset argument.
949  *
950  * @param start - start of the platform identifier (/proc/cpuinfo Hardware string, ro.product.board or ro.chipname)
951  *                to match.
952  * @param end - end of the platform identifier (/proc/cpuinfo Hardaware string, ro.product.board or ro.chipname) to
953  *              match.
954  * @param[out] chipset - location where chipset information will be stored upon a successful match.
955  *
956  * @returns true if signature matched, false otherwise.
957  */
match_pxa(const char * start,const char * end,struct cpuinfo_arm_chipset chipset[restrict static1])958 static bool match_pxa(
959 	const char* start, const char* end,
960 	struct cpuinfo_arm_chipset chipset[restrict static 1])
961 {
962 	/* Expect 6-7 symbols: "PXA" (3 symbols) + 3-4 digit model number */
963 	const size_t length = end - start;
964 	switch (length) {
965 		case 6:
966 		case 7:
967 			break;
968 		default:
969 			return false;
970 	}
971 
972 	/* Check that the string starts with "PXA". Symbols 1-3 are loaded and compared as little-endian 16-bit word. */
973 	if (start[0] != 'P') {
974 		return false;
975 	}
976 	const uint16_t expected_xa = load_u16le(start + 1);
977 	if (expected_xa != UINT16_C(0x4158) /* "AX" = reverse("XA") */) {
978 		return false;
979 	}
980 
981 	uint32_t model = 0;
982 
983 
984 	/* Check for a very common typo: "PXA1L88" for "PXA1088" */
985 	if (length == 7) {
986 		/* Load 4 model "number" symbols as a little endian 32-bit word and compare to "1L88" */
987 		const uint32_t expected_1L88 = load_u32le(start + 3);
988 		if (expected_1L88 == UINT32_C(0x38384C31) /* "88L1" = reverse("1L88") */) {
989 			model = 1088;
990 			goto write_chipset;
991 		}
992 	}
993 
994 	/* Check and parse 3-4 digit model number */
995 	for (uint32_t i = 3; i < length; i++) {
996 		const uint32_t digit = (uint32_t) (uint8_t) start[i] - '0';
997 		if (digit >= 10) {
998 			/* Not really a digit */
999 			return false;
1000 		}
1001 		model = model * 10 + digit;
1002 	}
1003 
1004 	/* Return parsed chipset. */
1005 write_chipset:
1006 	*chipset = (struct cpuinfo_arm_chipset) {
1007 		.vendor = cpuinfo_arm_chipset_vendor_marvell,
1008 		.series = cpuinfo_arm_chipset_series_marvell_pxa,
1009 		.model = model,
1010 	};
1011 	return true;
1012 }
1013 
1014 /**
1015  * Tries to match /BCM\d{4}$/ signature for Broadcom BCM chipsets.
1016  * If match successful, extracts model information into \p chipset argument.
1017  *
1018  * @param start - start of the /proc/cpuinfo Hardware string to match.
1019  * @param end - end of the /proc/cpuinfo Hardware string to match.
1020  * @param[out] chipset - location where chipset information will be stored upon a successful match.
1021  *
1022  * @returns true if signature matched, false otherwise.
1023  */
match_bcm(const char * start,const char * end,struct cpuinfo_arm_chipset chipset[restrict static1])1024 static bool match_bcm(
1025 	const char* start, const char* end,
1026 	struct cpuinfo_arm_chipset chipset[restrict static 1])
1027 {
1028 	/* Expect exactly 7 symbols: "BCM" (3 symbols) + 4-digit model number */
1029 	if (start + 7 != end) {
1030 		return false;
1031 	}
1032 
1033 	/* Check that the string starts with "BCM".
1034 	 * The first three characters are loaded and compared as a 24-bit little endian word.
1035 	 */
1036 	const uint32_t expected_bcm = load_u24le(start);
1037 	if (expected_bcm != UINT32_C(0x004D4342) /* "MCB" = reverse("BCM") */) {
1038 		return false;
1039 	}
1040 
1041 	/* Validate and parse 4-digit model number */
1042 	uint32_t model = 0;
1043 	for (uint32_t i = 3; i < 7; i++) {
1044 		const uint32_t digit = (uint32_t) (uint8_t) start[i] - '0';
1045 		if (digit >= 10) {
1046 			/* Not really a digit */
1047 			return false;
1048 		}
1049 		model = model * 10 + digit;
1050 	}
1051 
1052 	/* Return parsed chipset. */
1053 	*chipset = (struct cpuinfo_arm_chipset) {
1054 		.vendor = cpuinfo_arm_chipset_vendor_broadcom,
1055 		.series = cpuinfo_arm_chipset_series_broadcom_bcm,
1056 		.model = model,
1057 	};
1058 	return true;
1059 }
1060 
1061 /**
1062  * Tries to match /OMAP\d{4}$/ signature for Texas Instruments OMAP chipsets.
1063  * If match successful, extracts model information into \p chipset argument.
1064  *
1065  * @param start - start of the /proc/cpuinfo Hardware string to match.
1066  * @param end - end of the /proc/cpuinfo Hardware string to match.
1067  * @param[out] chipset - location where chipset information will be stored upon a successful match.
1068  *
1069  * @returns true if signature matched, false otherwise.
1070  */
match_omap(const char * start,const char * end,struct cpuinfo_arm_chipset chipset[restrict static1])1071 static bool match_omap(
1072 	const char* start, const char* end,
1073 	struct cpuinfo_arm_chipset chipset[restrict static 1])
1074 {
1075 	/* Expect exactly 8 symbols: "OMAP" (4 symbols) + 4-digit model number */
1076 	if (start + 8 != end) {
1077 		return false;
1078 	}
1079 
1080 	/* Check that the string starts with "OMAP". Symbols 0-4 are loaded and compared as little-endian 32-bit word. */
1081 	const uint32_t expected_omap = load_u32le(start);
1082 	if (expected_omap != UINT32_C(0x50414D4F) /* "PAMO" = reverse("OMAP") */) {
1083 		return false;
1084 	}
1085 
1086 	/* Validate and parse 4-digit model number */
1087 	uint32_t model = 0;
1088 	for (uint32_t i = 4; i < 8; i++) {
1089 		const uint32_t digit = (uint32_t) (uint8_t) start[i] - '0';
1090 		if (digit >= 10) {
1091 			/* Not really a digit */
1092 			return false;
1093 		}
1094 		model = model * 10 + digit;
1095 	}
1096 
1097 	/* Return parsed chipset. */
1098 	*chipset = (struct cpuinfo_arm_chipset) {
1099 		.vendor = cpuinfo_arm_chipset_vendor_texas_instruments,
1100 		.series = cpuinfo_arm_chipset_series_texas_instruments_omap,
1101 		.model = model,
1102 	};
1103 	return true;
1104 }
1105 
1106 /**
1107  * Compares platform identifier string to known values for Broadcom chipsets.
1108  * If the string matches one of the known values, the function decodes Broadcom chipset from frequency and number of
1109  * cores into \p chipset argument.
1110  *
1111  * @param start - start of the platform identifier (ro.product.board or ro.board.platform) to match.
1112  * @param end - end of the platform identifier (ro.product.board or ro.board.platform) to match.
1113  * @param cores - number of cores in the chipset.
1114  * @param max_cpu_freq_max - maximum of /sys/devices/system/cpu/cpu<number>/cpofreq/cpu_freq_max values.
1115  * @param[out] chipset - location where chipset information will be stored upon a successful match and decoding.
1116  *
1117  * @returns true if signature matched (even if exact model can't be decoded), false otherwise.
1118  */
match_and_parse_broadcom(const char * start,const char * end,uint32_t cores,uint32_t max_cpu_freq_max,struct cpuinfo_arm_chipset chipset[restrict static1])1119 static bool match_and_parse_broadcom(
1120 	const char* start, const char* end, uint32_t cores, uint32_t max_cpu_freq_max,
1121 	struct cpuinfo_arm_chipset chipset[restrict static 1])
1122 {
1123 	/* Expect 4-6 symbols: "java" (4 symbols), "rhea" (4 symbols), "capri" (5 symbols), or "hawaii" (6 symbols) */
1124 	const size_t length = end - start;
1125 	switch (length) {
1126 		case 4:
1127 		case 5:
1128 		case 6:
1129 			break;
1130 		default:
1131 			return false;
1132 	}
1133 
1134 	/*
1135 	 * Compare the platform identifier to known values for Broadcom chipsets:
1136 	 * - "rhea"
1137 	 * - "java"
1138 	 * - "capri"
1139 	 * - "hawaii"
1140 	 * Upon a successful match, decode chipset name from frequency and number of cores.
1141 	 */
1142 	uint32_t model = 0;
1143 	char suffix = 0;
1144 	const uint32_t expected_platform = load_u32le(start);
1145 	switch (expected_platform) {
1146 		case UINT32_C(0x61656872): /* "aehr" = reverse("rhea") */
1147 			if (length == 4) {
1148 				/*
1149 				 * Detected "rhea" platform:
1150 				 * - 1 core @ 849999 KHz -> BCM21654
1151 				 * - 1 core @ 999999 KHz -> BCM21654G
1152 				 */
1153 				if (cores == 1) {
1154 					model = 21654;
1155 					if (max_cpu_freq_max >= 999999) {
1156 						suffix = 'G';
1157 					}
1158 				}
1159 			}
1160 			break;
1161 		case UINT32_C(0x6176616A): /* "avaj" = reverse("java") */
1162 			if (length == 4) {
1163 				/*
1164 				 * Detected "java" platform:
1165 				 * - 4 cores -> BCM23550
1166 				 */
1167 				if (cores == 4) {
1168 					model = 23550;
1169 				}
1170 			}
1171 			break;
1172 		case UINT32_C(0x61776168): /* "awah" = reverse("hawa") */
1173 			if (length == 6) {
1174 				/* Check that string equals "hawaii" */
1175 				const uint16_t expected_ii = load_u16le(start + 4);
1176 				if (expected_ii == UINT16_C(0x6969) /* "ii" */ ) {
1177 					/*
1178 					 * Detected "hawaii" platform:
1179 					 * - 1 core -> BCM21663
1180 					 * - 2 cores @ 999999 KHz -> BCM21664
1181 					 * - 2 cores @ 1200000 KHz -> BCM21664T
1182 					 */
1183 					switch (cores) {
1184 						case 1:
1185 							model = 21663;
1186 							break;
1187 						case 2:
1188 							model = 21664;
1189 							if (max_cpu_freq_max >= 1200000) {
1190 								suffix = 'T';
1191 							}
1192 							break;
1193 					}
1194 				}
1195 			}
1196 			break;
1197 		case UINT32_C(0x72706163): /* "rpac" = reverse("capr") */
1198 			if (length == 5) {
1199 				/* Check that string equals "capri" */
1200 				if (start[4] == 'i') {
1201 					/*
1202 					 * Detected "capri" platform:
1203 					 * - 2 cores -> BCM28155
1204 					 */
1205 					if (cores == 2) {
1206 						model = 28155;
1207 					}
1208 				}
1209 			}
1210 			break;
1211 	}
1212 
1213 	if (model != 0) {
1214 		/* Chipset was successfully decoded */
1215 		*chipset = (struct cpuinfo_arm_chipset) {
1216 			.vendor = cpuinfo_arm_chipset_vendor_broadcom,
1217 			.series = cpuinfo_arm_chipset_series_broadcom_bcm,
1218 			.model = model,
1219 			.suffix = {
1220 				[0] = suffix,
1221 			},
1222 		};
1223 	}
1224 	return model != 0;
1225 }
1226 
1227 struct sunxi_map_entry {
1228 	uint8_t sunxi;
1229 	uint8_t cores;
1230 	uint8_t model;
1231 	char suffix;
1232 };
1233 
1234 static const struct sunxi_map_entry sunxi_map_entries[] = {
1235 #if CPUINFO_ARCH_ARM
1236 	{
1237 		/* ("sun4i", 1) -> "A10" */
1238 		.sunxi = 4,
1239 		.cores = 1,
1240 		.model = 10,
1241 	},
1242 	{
1243 		/* ("sun5i", 1) -> "A13" */
1244 		.sunxi = 5,
1245 		.cores = 1,
1246 		.model = 13,
1247 	},
1248 	{
1249 		/* ("sun6i", 4) -> "A31" */
1250 		.sunxi = 6,
1251 		.cores = 4,
1252 		.model = 31,
1253 	},
1254 	{
1255 		/* ("sun7i", 2) -> "A20" */
1256 		.sunxi = 7,
1257 		.cores = 2,
1258 		.model = 20,
1259 
1260 	},
1261 	{
1262 		/* ("sun8i", 2) -> "A23" */
1263 		.sunxi = 8,
1264 		.cores = 2,
1265 		.model = 23,
1266 	},
1267 	{
1268 		/* ("sun8i", 4) -> "A33" */
1269 		.sunxi = 8,
1270 		.cores = 4,
1271 		.model = 33,
1272 	},
1273 	{
1274 		/* ("sun8i", 8) -> "A83T" */
1275 		.sunxi = 8,
1276 		.cores = 8,
1277 		.model = 83,
1278 		.suffix = 'T',
1279 	},
1280 	{
1281 		/* ("sun9i", 8) -> "A80" */
1282 		.sunxi = 9,
1283 		.cores = 8,
1284 		.model = 80,
1285 	},
1286 #endif /* CPUINFO_ARCH_ARM */
1287 	{
1288 		/* ("sun50i", 4) -> "A64" */
1289 		.sunxi = 50,
1290 		.cores = 4,
1291 		.model = 64,
1292 	},
1293 };
1294 
1295 /**
1296  * Tries to match /proc/cpuinfo Hardware string to Allwinner /sun\d+i/ signature.
1297  * If the string matches signature, the function decodes Allwinner chipset from the number in the signature and the
1298  * number of cores, and stores it in \p chipset argument.
1299  *
1300  * @param start - start of the /proc/cpuinfo Hardware string to match.
1301  * @param end - end of the /proc/cpuinfo Hardware string to match.
1302  * @param cores - number of cores in the chipset.
1303  * @param[out] chipset - location where chipset information will be stored upon a successful match and decoding.
1304  *
1305  * @returns true if signature matched (even if exact model can't be decoded), false otherwise.
1306  */
match_and_parse_sunxi(const char * start,const char * end,uint32_t cores,struct cpuinfo_arm_chipset chipset[restrict static1])1307 static bool match_and_parse_sunxi(
1308 	const char* start, const char* end, uint32_t cores,
1309 	struct cpuinfo_arm_chipset chipset[restrict static 1])
1310 {
1311 	/* Expect at least 5 symbols: "sun" (3 symbols) + platform id (1-2 digits) + "i" (1 symbol) */
1312 	if (start + 5 > end) {
1313 		return false;
1314 	}
1315 
1316 	/* Compare the first 3 characters to "sun" */
1317 	if (start[0] != 's') {
1318 		return false;
1319 	}
1320 	const uint16_t expected_un = load_u16le(start + 1);
1321 	if (expected_un != UINT16_C(0x6E75) /* "nu" = reverse("un") */) {
1322 		return false;
1323 	}
1324 
1325 	/* Check and parse the first (required) digit of the sunXi platform id */
1326 	uint32_t sunxi_platform = 0;
1327 	{
1328 		const uint32_t digit = (uint32_t) (uint8_t) start[3] - '0';
1329 		if (digit >= 10) {
1330 			/* Not really a digit */
1331 			return false;
1332 		}
1333 		sunxi_platform = digit;
1334 	}
1335 
1336 	/* Parse optional second digit of the sunXi platform id */
1337 	const char* pos = start + 4;
1338 	{
1339 		const uint32_t digit = (uint32_t) (uint8_t) (*pos) - '0';
1340 		if (digit < 10) {
1341 			sunxi_platform = sunxi_platform * 10 + digit;
1342 			if (++pos == end) {
1343 				/* Expected one more character, final 'i' letter */
1344 				return false;
1345 			}
1346 		}
1347 	}
1348 
1349 	/* Validate the final 'i' letter */
1350 	if (*pos != 'i') {
1351 		return false;
1352 	}
1353 
1354 	/* Compare sunXi platform id and number of cores to tabluted values to decode chipset name */
1355 	uint32_t model = 0;
1356 	char suffix = 0;
1357 	for (size_t i = 0; i < CPUINFO_COUNT_OF(sunxi_map_entries); i++) {
1358 		if (sunxi_platform == sunxi_map_entries[i].sunxi && cores == sunxi_map_entries[i].cores) {
1359 			model = sunxi_map_entries[i].model;
1360 			suffix = sunxi_map_entries[i].suffix;
1361 			break;
1362 		}
1363 	}
1364 
1365 	if (model == 0) {
1366 		cpuinfo_log_info("unrecognized %"PRIu32"-core Allwinner sun%"PRIu32" platform", cores, sunxi_platform);
1367 	}
1368 	/* Create chipset name from decoded data */
1369 	*chipset = (struct cpuinfo_arm_chipset) {
1370 		.vendor = cpuinfo_arm_chipset_vendor_allwinner,
1371 		.series = cpuinfo_arm_chipset_series_allwinner_a,
1372 		.model = model,
1373 		.suffix = {
1374 			[0] = suffix,
1375 		},
1376 	};
1377 	return true;
1378 }
1379 
1380 /**
1381  * Compares /proc/cpuinfo Hardware string to "WMT" signature.
1382  * If the string matches signature, the function decodes WonderMedia chipset from frequency and number of cores into
1383  * \p chipset argument.
1384  *
1385  * @param start - start of the /proc/cpuinfo Hardware string to match.
1386  * @param end - end of the /proc/cpuinfo Hardware string to match.
1387  * @param cores - number of cores in the chipset.
1388  * @param max_cpu_freq_max - maximum of /sys/devices/system/cpu/cpu<number>/cpofreq/cpu_freq_max values.
1389  * @param[out] chipset - location where chipset information will be stored upon a successful match and decoding.
1390  *
1391  * @returns true if signature matched (even if exact model can't be decoded), false otherwise.
1392  */
match_and_parse_wmt(const char * start,const char * end,uint32_t cores,uint32_t max_cpu_freq_max,struct cpuinfo_arm_chipset chipset[restrict static1])1393 static bool match_and_parse_wmt(
1394 	const char* start, const char* end, uint32_t cores, uint32_t max_cpu_freq_max,
1395 	struct cpuinfo_arm_chipset chipset[restrict static 1])
1396 {
1397 	/* Expected 3 symbols: "WMT" */
1398 	if (start + 3 != end) {
1399 		return false;
1400 	}
1401 
1402 	/* Compare string to "WMT" */
1403 	if (start[0] != 'W') {
1404 		return false;
1405 	}
1406 	const uint16_t expected_mt = load_u16le(start + 1);
1407 	if (expected_mt != UINT16_C(0x544D) /* "TM" = reverse("MT") */) {
1408 		return false;
1409 	}
1410 
1411 	/* Decode chipset name from frequency and number of cores */
1412 	uint32_t model = 0;
1413 	switch (cores) {
1414 		case 1:
1415 			switch (max_cpu_freq_max) {
1416 				case 1008000:
1417 					/* 1 core @ 1008000 KHz -> WM8950 */
1418 					model = 8950;
1419 					break;
1420 				case 1200000:
1421 					/* 1 core @ 1200000 KHz -> WM8850 */
1422 					model = 8850;
1423 					break;
1424 			}
1425 			break;
1426 		case 2:
1427 			if (max_cpu_freq_max == 1500000) {
1428 				/* 2 cores @ 1500000 KHz -> WM8880 */
1429 				model = 8880;
1430 			}
1431 			break;
1432 	}
1433 
1434 	if (model == 0) {
1435 		cpuinfo_log_info("unrecognized WonderMedia platform with %"PRIu32" cores at %"PRIu32" KHz",
1436 			cores, max_cpu_freq_max);
1437 	}
1438 	*chipset = (struct cpuinfo_arm_chipset) {
1439 		.vendor = cpuinfo_arm_chipset_vendor_wondermedia,
1440 		.series = cpuinfo_arm_chipset_series_wondermedia_wm,
1441 		.model = model,
1442 	};
1443 	return true;
1444 }
1445 
1446 struct huawei_map_entry {
1447 	uint32_t platform;
1448 	uint32_t model;
1449 };
1450 
1451 static const struct huawei_map_entry huawei_platform_map[] = {
1452 	{
1453 		/* "ALP" -> Kirin 970 */
1454 		.platform = UINT32_C(0x00504C41), /* "\0PLA" = reverse("ALP\0") */
1455 		.model = 970,
1456 	},
1457 	{
1458 		/* "BAC" -> Kirin 659 */
1459 		.platform = UINT32_C(0x00434142), /* "\0CAB" = reverse("BAC\0") */
1460 		.model = 659,
1461 	},
1462 	{
1463 		/* "BLA" -> Kirin 970 */
1464 		.platform = UINT32_C(0x00414C42), /* "\0ALB" = reverse("BLA\0") */
1465 		.model = 970,
1466 	},
1467 	{
1468 		/* "BKL" -> Kirin 970 */
1469 		.platform = UINT32_C(0x004C4B42), /* "\0LKB" = reverse("BKL\0") */
1470 		.model = 970,
1471 	},
1472 	{
1473 		/* "CLT" -> Kirin 970 */
1474 		.platform = UINT32_C(0x00544C43), /* "\0TLC" = reverse("CLT\0") */
1475 		.model = 970,
1476 	},
1477 	{
1478 		/* "COL" -> Kirin 970 */
1479 		.platform = UINT32_C(0x004C4F43), /* "\0LOC" = reverse("COL\0") */
1480 		.model = 970,
1481 	},
1482 	{
1483 		/* "COR" -> Kirin 970 */
1484 		.platform = UINT32_C(0x00524F43), /* "\0ROC" = reverse("COR\0") */
1485 		.model = 970,
1486 	},
1487 	{
1488 		/* "DUK" -> Kirin 960 */
1489 		.platform = UINT32_C(0x004B5544), /* "\0KUD" = reverse("DUK\0") */
1490 		.model = 960,
1491 	},
1492 	{
1493 		/* "EML" -> Kirin 970 */
1494 		.platform = UINT32_C(0x004C4D45), /* "\0LME" = reverse("EML\0") */
1495 		.model = 970,
1496 	},
1497 	{
1498 		/* "EVA" -> Kirin 955 */
1499 		.platform = UINT32_C(0x00415645), /* "\0AVE" = reverse("EVA\0") */
1500 		.model = 955,
1501 	},
1502 	{
1503 		/* "FRD" -> Kirin 950 */
1504 		.platform = UINT32_C(0x00445246), /* "\0DRF" = reverse("FRD\0") */
1505 		.model = 950,
1506 	},
1507 	{
1508 		/* "INE" -> Kirin 710 */
1509 		.platform = UINT32_C(0x00454E49), /* "\0ENI" = reverse("INE\0") */
1510 		.model = 710,
1511 	},
1512 	{
1513 		/* "KNT" -> Kirin 950 */
1514 		.platform = UINT32_C(0x00544E4B), /* "\0TNK" = reverse("KNT\0") */
1515 		.model = 950,
1516 	},
1517 	{
1518 		/* "LON" -> Kirin 960 */
1519 		.platform = UINT32_C(0x004E4F4C), /* "\0NOL" = reverse("LON\0") */
1520 		.model = 960,
1521 	},
1522 	{
1523 		/* "LYA" -> Kirin 980 */
1524 		.platform = UINT32_C(0x0041594C), /* "\0AYL" = reverse("LYA\0") */
1525 		.model = 980,
1526 	},
1527 	{
1528 		/* "MCN" -> Kirin 980 */
1529 		.platform = UINT32_C(0x004E434D), /* "\0NCM" = reverse("MCN\0") */
1530 		.model = 980,
1531 	},
1532 	{
1533 		/* "MHA" -> Kirin 960 */
1534 		.platform = UINT32_C(0x0041484D), /* "\0AHM" = reverse("MHA\0") */
1535 		.model = 960,
1536 	},
1537 	{
1538 		/* "NEO" -> Kirin 970 */
1539 		.platform = UINT32_C(0x004F454E), /* "\0OEN" = reverse("NEO\0") */
1540 		.model = 970,
1541 	},
1542 	{
1543 		/* "NXT" -> Kirin 950 */
1544 		.platform = UINT32_C(0x0054584E), /* "\0TXN" = reverse("NXT\0") */
1545 		.model = 950,
1546 	},
1547 	{
1548 		/* "PAN" -> Kirin 980 */
1549 		.platform = UINT32_C(0x004E4150), /* "\0NAP" = reverse("PAN\0") */
1550 		.model = 980,
1551 	},
1552 	{
1553 		/* "PAR" -> Kirin 970 */
1554 		.platform = UINT32_C(0x00524150), /* "\0RAP" = reverse("PAR\0") */
1555 		.model = 970,
1556 	},
1557 	{
1558 		/* "RVL" -> Kirin 970 */
1559 		.platform = UINT32_C(0x004C5652), /* "\0LVR" = reverse("RVL\0") */
1560 		.model = 970,
1561 	},
1562 	{
1563 		/* "STF" -> Kirin 960 */
1564 		.platform = UINT32_C(0x00465453), /* "\0FTS" = reverse("STF\0") */
1565 		.model = 960,
1566 	},
1567 	{
1568 		/* "SUE" -> Kirin 980 */
1569 		.platform = UINT32_C(0x00455553), /* "\0EUS" = reverse("SUE\0") */
1570 		.model = 980,
1571 	},
1572 	{
1573 		/* "VIE" -> Kirin 955 */
1574 		.platform = UINT32_C(0x00454956), /* "\0EIV" = reverse("VIE\0") */
1575 		.model = 955,
1576 	},
1577 	{
1578 		/* "VKY" -> Kirin 960 */
1579 		.platform = UINT32_C(0x00594B56), /* "\0YKV" = reverse("VKY\0") */
1580 		.model = 960,
1581 	},
1582 	{
1583 		/* "VTR" -> Kirin 960 */
1584 		.platform = UINT32_C(0x00525456), /* "\0RTV" = reverse("VTR\0") */
1585 		.model = 960,
1586 	},
1587 };
1588 
1589 /**
1590  * Tries to match ro.product.board string to Huawei /([A-Z]{3})(\-[A-Z]?L\d{2})$/ signature where \1 is one of the
1591  * known values for Huawei devices, which do not report chipset name elsewhere.
1592  * If the string matches signature, the function decodes chipset (always HiSilicon Kirin for matched devices) from
1593  * the Huawei platform ID in the signature and stores it in \p chipset argument.
1594  *
1595  * @param start - start of the ro.product.board string to match.
1596  * @param end - end of the ro.product.board string to match.
1597  * @param[out] chipset - location where chipset information will be stored upon a successful match and decoding.
1598  *
1599  * @returns true if signature matched, false otherwise.
1600  */
match_and_parse_huawei(const char * start,const char * end,struct cpuinfo_arm_chipset chipset[restrict static1])1601 static bool match_and_parse_huawei(
1602 	const char* start, const char* end,
1603 	struct cpuinfo_arm_chipset chipset[restrict static 1])
1604 {
1605 	/*
1606 	 * Expect length of either 3, 7 or 8, exactly:
1607 	 * - 3-letter platform identifier (see huawei_platform_map)
1608 	 * - 3-letter platform identifier + '-' + 'L' + two digits
1609 	 * - 3-letter platform identifier + '-' + capital letter + 'L' + two digits
1610 	 */
1611 	const size_t length = end - start;
1612 	switch (length) {
1613 		case 3:
1614 		case 7:
1615 		case 8:
1616 			break;
1617 		default:
1618 			return false;
1619 	}
1620 
1621 	/*
1622 	 * Try to find the first three-letter substring in among the tabulated entries for Huawei devices.
1623 	 * The first three letters are loaded and compared as a little-endian 24-bit word.
1624 	 */
1625 	uint32_t model = 0;
1626 	const uint32_t target_platform_id = load_u24le(start);
1627 	for (uint32_t i = 0; i < CPUINFO_COUNT_OF(huawei_platform_map); i++) {
1628 		if (huawei_platform_map[i].platform == target_platform_id) {
1629 			model = huawei_platform_map[i].model;
1630 			break;
1631 		}
1632 	}
1633 
1634 	if (model == 0) {
1635 		/* Platform does not match the tabulated Huawei entries */
1636 		return false;
1637 	}
1638 
1639 	if (length > 3) {
1640 		/*
1641 		 * Check that:
1642 		 * - The symbol after platform id is a dash
1643 		 * - The symbol after it is an uppercase letter. For 7-symbol strings, the symbol is just 'L'.
1644 		 */
1645 		if (start[3] != '-' || !is_ascii_alphabetic_uppercase(start[4])) {
1646 			return false;
1647 		}
1648 
1649 		/* Check that the last 3 entries are /L\d\d/ */
1650 		if (end[-3] != 'L' || !is_ascii_numeric(end[-2]) || !is_ascii_numeric(end[-1])) {
1651 			return false;
1652 		}
1653 	}
1654 
1655 	/* All checks succeeded, commit chipset name */
1656 	*chipset = (struct cpuinfo_arm_chipset) {
1657 		.vendor = cpuinfo_arm_chipset_vendor_hisilicon,
1658 		.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
1659 		.model = model,
1660 	};
1661 	return true;
1662 }
1663 
1664 /**
1665  * Tries to match /tcc\d{3}x$/ signature for Telechips TCCXXXx chipsets.
1666  * If match successful, extracts model information into \p chipset argument.
1667  *
1668  * @param start - start of the /proc/cpuinfo Hardware string to match.
1669  * @param end - end of the /proc/cpuinfo Hardware string to match.
1670  * @param[out] chipset - location where chipset information will be stored upon a successful match.
1671  *
1672  * @returns true if signature matched, false otherwise.
1673  */
match_tcc(const char * start,const char * end,struct cpuinfo_arm_chipset chipset[restrict static1])1674 static bool match_tcc(
1675 	const char* start, const char* end,
1676 	struct cpuinfo_arm_chipset chipset[restrict static 1])
1677 {
1678 	/* Expect exactly 7 symbols: "tcc" (3 symbols) + 3-digit model number + fixed "x" suffix */
1679 	if (start + 7 != end) {
1680 		return false;
1681 	}
1682 
1683 	/* Quick check for the first character */
1684 	if (start[0] != 't') {
1685 		return false;
1686 	}
1687 
1688 	/* Load the next 2 bytes as little endian 16-bit word */
1689 	const uint16_t expected_cc = load_u16le(start + 1);
1690 	if (expected_cc != UINT16_C(0x6363) /* "cc" */ ) {
1691 		return false;
1692 	}
1693 
1694 	/* Check and parse 3-digit model number */
1695 	uint32_t model = 0;
1696 	for (uint32_t i = 3; i < 6; i++) {
1697 		const uint32_t digit = (uint32_t) (uint8_t) start[i] - '0';
1698 		if (digit >= 10) {
1699 			/* Not really a digit */
1700 			return false;
1701 		}
1702 		model = model * 10 + digit;
1703 	}
1704 
1705 	/* Check the fixed 'x' suffix in the end */
1706 	if (start[6] != 'x') {
1707 		return false;
1708 	}
1709 
1710 	/* Commit parsed chipset. */
1711 	*chipset = (struct cpuinfo_arm_chipset) {
1712 		.vendor = cpuinfo_arm_chipset_vendor_telechips,
1713 		.series = cpuinfo_arm_chipset_series_telechips_tcc,
1714 		.model = model,
1715 		.suffix = {
1716 			[0] = 'X'
1717 		},
1718 	};
1719 	return true;
1720 }
1721 
1722 /*
1723  * Compares ro.board.platform string to Nvidia Tegra signatures ("tegra" and "tegra3")
1724  * This check has effect on how /proc/cpuinfo Hardware string is interpreted.
1725  *
1726  * @param start - start of the ro.board.platform string to check.
1727  * @param end - end of the ro.board.platform string to check.
1728  *
1729  * @returns true if the string matches an Nvidia Tegra signature, and false otherwise
1730  */
is_tegra(const char * start,const char * end)1731 static bool is_tegra(const char* start, const char* end) {
1732 	/* Expect 5 ("tegra") or 6 ("tegra3") symbols */
1733 	const size_t length = end - start;
1734 	switch (length) {
1735 		case 5:
1736 		case 6:
1737 			break;
1738 		default:
1739 			return false;
1740 	}
1741 
1742 	/* Check that the first 5 characters match "tegra" */
1743 	if (start[0] != 't') {
1744 		return false;
1745 	}
1746 	const uint32_t expected_egra = load_u32le(start + 1);
1747 	if (expected_egra != UINT32_C(0x61726765) /* "arge" = reverse("egra") */) {
1748 		return false;
1749 	}
1750 
1751 	/* Check if the string is either "tegra" (length = 5) or "tegra3" (length != 5) and last character is '3' */
1752 	return (length == 5 || start[5] == '3');
1753 }
1754 
1755 struct special_map_entry {
1756 	const char* platform;
1757 	uint16_t model;
1758 	uint8_t series;
1759 	char suffix;
1760 };
1761 
1762 static const struct special_map_entry special_hardware_map_entries[] = {
1763 #if CPUINFO_ARCH_ARM
1764 	{
1765 		/* "k3v2oem1" -> HiSilicon K3V2 */
1766 		.platform = "k3v2oem1",
1767 		.series = cpuinfo_arm_chipset_series_hisilicon_k3v,
1768 		.model = 2,
1769 	},
1770 	{
1771 		/* "hi6620oem" -> HiSilicon Kirin 910T */
1772 		.platform = "hi6620oem",
1773 		.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
1774 		.model = 910,
1775 		.suffix = 'T'
1776 	},
1777 #endif /* CPUINFO_ARCH_ARM */
1778 	{
1779 		/* "hi6250" -> HiSilicon Kirin 650 */
1780 		.platform = "hi6250",
1781 		.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
1782 		.model = 650,
1783 	},
1784 	{
1785 		/* "hi6210sft" -> HiSilicon Kirin 620 */
1786 		.platform = "hi6210sft",
1787 		.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
1788 		.model = 620,
1789 	},
1790 	{
1791 		/* "hi3751" -> HiSilicon Hi3751 */
1792 		.platform = "hi3751",
1793 		.series = cpuinfo_arm_chipset_series_hisilicon_hi,
1794 		.model = 3751,
1795 	},
1796 #if CPUINFO_ARCH_ARM
1797 	{
1798 		/* "hi3630" -> HiSilicon Kirin 920 */
1799 		.platform = "hi3630",
1800 		.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
1801 		.model = 920,
1802 	},
1803 #endif /* CPUINFO_ARCH_ARM */
1804 	{
1805 		/* "hi3635" -> HiSilicon Kirin 930 */
1806 		.platform = "hi3635",
1807 		.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
1808 		.model = 930,
1809 	},
1810 #if CPUINFO_ARCH_ARM
1811 	{
1812 		/* "gs702a" -> Actions ATM7029 (Cortex-A5 + GC1000) */
1813 		.platform = "gs702a",
1814 		.series = cpuinfo_arm_chipset_series_actions_atm,
1815 		.model = 7029,
1816 	},
1817 	{
1818 		/* "gs702c" -> Actions ATM7029B (Cortex-A5 + SGX540) */
1819 		.platform = "gs702c",
1820 		.series = cpuinfo_arm_chipset_series_actions_atm,
1821 		.model = 7029,
1822 		.suffix = 'B',
1823 	},
1824 	{
1825 		/* "gs703d" -> Actions ATM7039S */
1826 		.platform = "gs703d",
1827 		.series = cpuinfo_arm_chipset_series_actions_atm,
1828 		.model = 7039,
1829 		.suffix = 'S',
1830 	},
1831 	{
1832 		/* "gs705a" -> Actions ATM7059A */
1833 		.platform = "gs705a",
1834 		.series = cpuinfo_arm_chipset_series_actions_atm,
1835 		.model = 7059,
1836 		.suffix = 'A',
1837 	},
1838 	{
1839 		/* "Amlogic Meson8" -> Amlogic S812 */
1840 		.platform = "Amlogic Meson8",
1841 		.series = cpuinfo_arm_chipset_series_amlogic_s,
1842 		.model = 812,
1843 	},
1844 	{
1845 		/* "Amlogic Meson8B" -> Amlogic S805 */
1846 		.platform = "Amlogic Meson8B",
1847 		.series = cpuinfo_arm_chipset_series_amlogic_s,
1848 		.model = 805,
1849 	},
1850 	{
1851 		/* "mapphone_CDMA" -> Texas Instruments OMAP4430 */
1852 		.platform = "mapphone_CDMA",
1853 		.series = cpuinfo_arm_chipset_series_texas_instruments_omap,
1854 		.model = 4430,
1855 	},
1856 	{
1857 		/* "Superior" -> Texas Instruments OMAP4470 */
1858 		.platform = "Superior",
1859 		.series = cpuinfo_arm_chipset_series_texas_instruments_omap,
1860 		.model = 4470,
1861 	},
1862 	{
1863 		/* "Tuna" (Samsung Galaxy Nexus) -> Texas Instruments OMAP4460 */
1864 		.platform = "Tuna",
1865 		.series = cpuinfo_arm_chipset_series_texas_instruments_omap,
1866 		.model = 4460,
1867 	},
1868 	{
1869 		/* "Manta" (Samsung Nexus 10) -> Samsung Exynos 5250 */
1870 		.platform = "Manta",
1871 		.series = cpuinfo_arm_chipset_series_samsung_exynos,
1872 		.model = 5250,
1873 	},
1874 	{
1875 		/* "Odin" -> LG Nuclun 7111 */
1876 		.platform = "Odin",
1877 		.series = cpuinfo_arm_chipset_series_lg_nuclun,
1878 		.model = 7111,
1879 	},
1880 	{
1881 		/* "Madison" -> MStar 6A338 */
1882 		.platform = "Madison",
1883 		.series = cpuinfo_arm_chipset_series_mstar_6a,
1884 		.model = 338,
1885 	},
1886 #endif /* CPUINFO_ARCH_ARM */
1887 };
1888 
1889 static const struct special_map_entry tegra_hardware_map_entries[] = {
1890 #if CPUINFO_ARCH_ARM
1891 	{
1892 		/* "cardhu" (Nvidia Cardhu developer tablet) -> Tegra T30 */
1893 		.platform = "cardhu",
1894 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
1895 		.model = 30,
1896 	},
1897 	{
1898 		/* "kai" -> Tegra T30L */
1899 		.platform = "kai",
1900 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
1901 		.model = 30,
1902 		.suffix = 'L',
1903 	},
1904 	{
1905 		/* "p3" (Samsung Galaxy Tab 8.9) -> Tegra T20 */
1906 		.platform = "p3",
1907 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
1908 		.model = 20,
1909 	},
1910 	{
1911 		/* "n1" (Samsung Galaxy R / Samsung Captivate Glide) -> Tegra AP20H */
1912 		.platform = "n1",
1913 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_ap,
1914 		.model = 20,
1915 		.suffix = 'H',
1916 	},
1917 	{
1918 		/* "SHW-M380S" (Samsung Galaxy Tab 10.1) -> Tegra T20 */
1919 		.platform = "SHW-M380S",
1920 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
1921 		.model = 20,
1922 	},
1923 	{
1924 		/* "m470" (Hisense Sero 7 Pro) -> Tegra T30L */
1925 		.platform = "m470",
1926 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
1927 		.model = 30,
1928 		.suffix = 'L',
1929 	},
1930 	{
1931 		/* "endeavoru" (HTC One X) -> Tegra AP33 */
1932 		.platform = "endeavoru",
1933 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_ap,
1934 		.model = 33,
1935 	},
1936 	{
1937 		/* "evitareul" (HTC One X+) -> Tegra T33 */
1938 		.platform = "evitareul",
1939 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
1940 		.model = 33,
1941 	},
1942 	{
1943 		/* "enrc2b" (HTC One X+) -> Tegra T33 */
1944 		.platform = "enrc2b",
1945 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
1946 		.model = 33,
1947 	},
1948 	{
1949 		/* "mozart" (Asus Transformer Pad TF701T) -> Tegra T114 */
1950 		.platform = "mozart",
1951 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
1952 		.model = 114,
1953 	},
1954 	{
1955 		/* "tegratab" (Tegra Note 7) -> Tegra T114 */
1956 		.platform = "tegratab",
1957 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
1958 		.model = 114,
1959 	},
1960 	{
1961 		/* "tn8" (Nvidia Shield Tablet K1) -> Tegra T124 */
1962 		.platform = "tn8",
1963 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
1964 		.model = 124,
1965 	},
1966 	{
1967 		/* "roth" (Nvidia Shield Portable) -> Tegra T114 */
1968 		.platform = "roth",
1969 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
1970 		.model = 114,
1971 	},
1972 	{
1973 		/* "pisces" (Xiaomi Mi 3) -> Tegra T114 */
1974 		.platform = "pisces",
1975 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
1976 		.model = 114,
1977 	},
1978 	{
1979 		/* "mocha" (Xiaomi Mi Pad) -> Tegra T124 */
1980 		.platform = "mocha",
1981 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
1982 		.model = 124,
1983 	},
1984 	{
1985 		/* "stingray" (Motorola XOOM) -> Tegra AP20H */
1986 		.platform = "stingray",
1987 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_ap,
1988 		.model = 20,
1989 		.suffix = 'H',
1990 	},
1991 	{
1992 		/* "Ceres" (Wiko Highway 4G) -> Tegra SL460N */
1993 		.platform = "Ceres",
1994 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_sl,
1995 		.model = 460,
1996 		.suffix = 'N',
1997 	},
1998 	{
1999 		/* "MT799" (nabi 2 Tablet) -> Tegra T30 */
2000 		.platform = "MT799",
2001 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2002 		.model = 30,
2003 	},
2004 	{
2005 		/* "t8400n" (nabi DreamTab HD8) -> Tegra T114 */
2006 		.platform = "t8400n",
2007 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2008 		.model = 114,
2009 	},
2010 	{
2011 		/* "chagall" (Fujitsu Stylistic M532) -> Tegra T30 */
2012 		.platform = "chagall",
2013 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2014 		.model = 30,
2015 	},
2016 	{
2017 		/* "ventana" (Asus Transformer TF101) -> Tegra T20 */
2018 		.platform = "ventana",
2019 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2020 		.model = 20,
2021 	},
2022 	{
2023 		/* "bobsleigh" (Fujitsu Arrows Tab F-05E) -> Tegra T33 */
2024 		.platform = "bobsleigh",
2025 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2026 		.model = 33,
2027 	},
2028 	{
2029 		/* "tegra_fjdev101" (Fujitsu Arrows X F-10D) -> Tegra AP33 */
2030 		.platform = "tegra_fjdev101",
2031 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_ap,
2032 		.model = 33,
2033 	},
2034 	{
2035 		/* "tegra_fjdev103" (Fujitsu Arrows V F-04E) -> Tegra T33 */
2036 		.platform = "tegra_fjdev103",
2037 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2038 		.model = 33,
2039 	},
2040 	{
2041 		/* "nbx03" (Sony Tablet S) -> Tegra T20 */
2042 		.platform = "nbx03",
2043 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2044 		.model = 20,
2045 	},
2046 	{
2047 		/* "txs03" (Sony Xperia Tablet S) -> Tegra T30L */
2048 		.platform = "txs03",
2049 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2050 		.model = 30,
2051 		.suffix = 'L',
2052 	},
2053 	{
2054 		/* "x3" (LG Optimus 4X HD P880) -> Tegra AP33 */
2055 		.platform = "x3",
2056 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_ap,
2057 		.model = 33,
2058 	},
2059 	{
2060 		/* "vu10" (LG Optimus Vu P895) -> Tegra AP33 */
2061 		.platform = "vu10",
2062 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_ap,
2063 		.model = 33,
2064 	},
2065 	{
2066 		/* "BIRCH" (HP Slate 7 Plus) -> Tegra T30L */
2067 		.platform = "BIRCH",
2068 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2069 		.model = 30,
2070 		.suffix = 'L',
2071 	},
2072 	{
2073 		/* "macallan" (HP Slate 8 Pro) -> Tegra T114 */
2074 		.platform = "macallan",
2075 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2076 		.model = 114,
2077 	},
2078 	{
2079 		/* "maya" (HP SlateBook 10 x2) -> Tegra T114 */
2080 		.platform = "maya",
2081 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2082 		.model = 114,
2083 	},
2084 	{
2085 		/* "antares" (Toshiba AT100) -> Tegra T20 */
2086 		.platform = "antares",
2087 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2088 		.model = 20,
2089 	},
2090 	{
2091 		/* "tostab12AL" (Toshiba AT300SE "Excite 10 SE") -> Tegra T30L */
2092 		.platform = "tostab12AL",
2093 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2094 		.model = 30,
2095 		.suffix = 'L',
2096 	},
2097 	{
2098 		/* "tostab12BL" (Toshiba AT10-A "Excite Pure") -> Tegra T30L */
2099 		.platform = "tostab12BL",
2100 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2101 		.model = 30,
2102 		.suffix = 'L',
2103 	},
2104 	{
2105 		/* "sphinx" (Toshiba AT270 "Excite 7.7") -> Tegra T30 */
2106 		.platform = "sphinx",
2107 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2108 		.model = 30,
2109 	},
2110 	{
2111 		/* "tostab11BS" (Toshiba AT570 "Regza 7.7") -> Tegra T30 */
2112 		.platform = "tostab11BS",
2113 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2114 		.model = 30,
2115 	},
2116 	{
2117 		/* "tostab12BA" (Toshiba AT10-LE-A "Excite Pro") -> Tegra T114 */
2118 		.platform = "tostab12BA",
2119 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2120 		.model = 114,
2121 	},
2122 	{
2123 		/* "vangogh" (Acer Iconia Tab A100) -> Tegra T20 */
2124 		.platform = "vangogh",
2125 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2126 		.model = 20,
2127 	},
2128 	{
2129 		/* "a110" (Acer Iconia Tab A110) -> Tegra T30L */
2130 		.platform = "a110",
2131 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2132 		.model = 30,
2133 		.suffix = 'L',
2134 	},
2135 	{
2136 		/* "picasso_e" (Acer Iconia Tab A200) -> Tegra AP20H */
2137 		.platform = "picasso_e",
2138 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_ap,
2139 		.model = 20,
2140 		.suffix = 'H',
2141 	},
2142 	{
2143 		/* "picasso_e2" (Acer Iconia Tab A210) -> Tegra T30L */
2144 		.platform = "picasso_e2",
2145 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2146 		.model = 30,
2147 		.suffix = 'L',
2148 	},
2149 	{
2150 		/* "picasso" (Acer Iconia Tab A500) -> Tegra AP20H */
2151 		.platform = "picasso",
2152 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_ap,
2153 		.model = 20,
2154 		.suffix = 'H',
2155 	},
2156 	{
2157 		/* "picasso_m" (Acer Iconia Tab A510) -> Tegra T30 */
2158 		.platform = "picasso_m",
2159 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2160 		.model = 30,
2161 	},
2162 	{
2163 		/* "picasso_mf" (Acer Iconia Tab A700) -> Tegra T30 */
2164 		.platform = "picasso_mf",
2165 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2166 		.model = 30,
2167 	},
2168 	{
2169 		/* "avalon" (Toshiba AT300 "Excite 10") -> Tegra T30L */
2170 		.platform = "avalon",
2171 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2172 		.model = 30,
2173 		.suffix = 'L',
2174 	},
2175 	{
2176 		/* "NS_14T004" (iRiver NS-14T004) -> Tegra T30L */
2177 		.platform = "NS_14T004",
2178 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2179 		.model = 30,
2180 		.suffix = 'L',
2181 	},
2182 	{
2183 		/* "WIKIPAD" (Wikipad) -> Tegra T30 */
2184 		.platform = "WIKIPAD",
2185 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2186 		.model = 30,
2187 	},
2188 	{
2189 		/* "kb" (Pegatron Q00Q) -> Tegra T114 */
2190 		.platform = "kb",
2191 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2192 		.model = 114,
2193 	},
2194 #endif /* CPUINFO_ARCH_ARM */
2195 	{
2196 		/* "foster_e" (Nvidia Shield TV, Flash) -> Tegra T210 */
2197 		.platform = "foster_e",
2198 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2199 		.model = 210,
2200 	},
2201 	{
2202 		/* "foster_e_hdd" (Nvidia Shield TV, HDD) -> Tegra T210 */
2203 		.platform = "foster_e_hdd",
2204 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2205 		.model = 210,
2206 	},
2207 	{
2208 		/* "darcy" (Nvidia Shield TV 2017) -> Tegra T210 */
2209 		.platform = "darcy",
2210 		.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2211 		.model = 210,
2212 	},
2213 };
2214 
2215 /*
2216  * Decodes chipset name from /proc/cpuinfo Hardware string.
2217  * For some chipsets, the function relies frequency and on number of cores for chipset detection.
2218  *
2219  * @param[in] platform - /proc/cpuinfo Hardware string.
2220  * @param cores - number of cores in the chipset.
2221  * @param max_cpu_freq_max - maximum of /sys/devices/system/cpu/cpu<number>/cpofreq/cpu_freq_max values.
2222  *
2223  * @returns Decoded chipset name. If chipset could not be decoded, the resulting structure would use `unknown` vendor
2224  *          and series identifiers.
2225  */
cpuinfo_arm_linux_decode_chipset_from_proc_cpuinfo_hardware(const char hardware[restrict static CPUINFO_HARDWARE_VALUE_MAX],uint32_t cores,uint32_t max_cpu_freq_max,bool is_tegra)2226 struct cpuinfo_arm_chipset cpuinfo_arm_linux_decode_chipset_from_proc_cpuinfo_hardware(
2227 	const char hardware[restrict static CPUINFO_HARDWARE_VALUE_MAX],
2228 	uint32_t cores, uint32_t max_cpu_freq_max, bool is_tegra)
2229 {
2230 	struct cpuinfo_arm_chipset chipset;
2231 	const size_t hardware_length = strnlen(hardware, CPUINFO_HARDWARE_VALUE_MAX);
2232 	const char* hardware_end = hardware + hardware_length;
2233 
2234 	if (is_tegra) {
2235 		/*
2236 		 * Nvidia Tegra-specific path: compare /proc/cpuinfo Hardware string to
2237 		 * tabulated Hardware values for popular chipsets/devices with Tegra chipsets.
2238 		 * This path is only used when ro.board.platform indicates a Tegra chipset
2239 		 * (albeit does not indicate which exactly Tegra chipset).
2240 		 */
2241 		for (size_t i = 0; i < CPUINFO_COUNT_OF(tegra_hardware_map_entries); i++) {
2242 			if (strncmp(tegra_hardware_map_entries[i].platform, hardware, hardware_length) == 0 &&
2243 					tegra_hardware_map_entries[i].platform[hardware_length] == 0)
2244 			{
2245 				cpuinfo_log_debug(
2246 					"found /proc/cpuinfo Hardware string \"%.*s\" in Nvidia Tegra chipset table",
2247 					(int) hardware_length, hardware);
2248 				/* Create chipset name from entry */
2249 				return (struct cpuinfo_arm_chipset) {
2250 					.vendor = chipset_series_vendor[tegra_hardware_map_entries[i].series],
2251 					.series = (enum cpuinfo_arm_chipset_series) tegra_hardware_map_entries[i].series,
2252 					.model = tegra_hardware_map_entries[i].model,
2253 					.suffix = {
2254 						[0] = tegra_hardware_map_entries[i].suffix,
2255 					},
2256 				};
2257 			}
2258 		}
2259 	} else {
2260 		/* Generic path: consider all other vendors */
2261 
2262 		bool word_start = true;
2263 		for (const char* pos = hardware; pos != hardware_end; pos++) {
2264 			const char c = *pos;
2265 			switch (c) {
2266 				case ' ':
2267 				case '\t':
2268 				case ',':
2269 					word_start = true;
2270 					break;
2271 				default:
2272 					if (word_start && is_ascii_alphabetic(c)) {
2273 						/* Check Qualcomm MSM/APQ signature */
2274 						if (match_msm_apq(pos, hardware_end, &chipset)) {
2275 							cpuinfo_log_debug(
2276 								"matched Qualcomm MSM/APQ signature in /proc/cpuinfo Hardware string \"%.*s\"",
2277 								(int) hardware_length, hardware);
2278 							return chipset;
2279 						}
2280 
2281 						/* Check SDMxxx (Qualcomm Snapdragon) signature */
2282 						if (match_sdm(pos, hardware_end, &chipset)) {
2283 							cpuinfo_log_debug(
2284 								"matched Qualcomm SDM signature in /proc/cpuinfo Hardware string \"%.*s\"",
2285 								(int) hardware_length, hardware);
2286 							return chipset;
2287 						}
2288 
2289 						/* Check SMxxxx (Qualcomm Snapdragon) signature */
2290 						if (match_sm(pos, hardware_end, &chipset)) {
2291 							cpuinfo_log_debug(
2292 								"matched Qualcomm SM signature in /proc/cpuinfo Hardware string \"%.*s\"",
2293 								(int) hardware_length, hardware);
2294 							return chipset;
2295 						}
2296 
2297 						/* Check MediaTek MT signature */
2298 						if (match_mt(pos, hardware_end, true, &chipset)) {
2299 							cpuinfo_log_debug(
2300 								"matched MediaTek MT signature in /proc/cpuinfo Hardware string \"%.*s\"",
2301 								(int) hardware_length, hardware);
2302 							return chipset;
2303 						}
2304 
2305 						/* Check HiSilicon Kirin signature */
2306 						if (match_kirin(pos, hardware_end, &chipset)) {
2307 							cpuinfo_log_debug(
2308 								"matched HiSilicon Kirin signature in /proc/cpuinfo Hardware string \"%.*s\"",
2309 								(int) hardware_length, hardware);
2310 							return chipset;
2311 						}
2312 
2313 						/* Check Rockchip RK signature */
2314 						if (match_rk(pos, hardware_end, &chipset)) {
2315 							cpuinfo_log_debug(
2316 								"matched Rockchip RK signature in /proc/cpuinfo Hardware string \"%.*s\"",
2317 								(int) hardware_length, hardware);
2318 							return chipset;
2319 						}
2320 					}
2321 					word_start = false;
2322 					break;
2323 			}
2324 		}
2325 
2326 		/* Check Samsung Exynos signature */
2327 		if (match_samsung_exynos(hardware, hardware_end, &chipset)) {
2328 			cpuinfo_log_debug(
2329 				"matched Samsung Exynos signature in /proc/cpuinfo Hardware string \"%.*s\"",
2330 				(int) hardware_length, hardware);
2331 			return chipset;
2332 		}
2333 
2334 		/* Check universalXXXX (Samsung Exynos) signature */
2335 		if (match_universal(hardware, hardware_end, &chipset)) {
2336 			cpuinfo_log_debug(
2337 				"matched UNIVERSAL (Samsung Exynos) signature in /proc/cpuinfo Hardware string \"%.*s\"",
2338 				(int) hardware_length, hardware);
2339 			return chipset;
2340 		}
2341 
2342 		#if CPUINFO_ARCH_ARM
2343 			/* Match /SMDK(4410|4x12)$/ */
2344 			if (match_and_parse_smdk(hardware, hardware_end, cores, &chipset)) {
2345 				cpuinfo_log_debug(
2346 					"matched SMDK (Samsung Exynos) signature in /proc/cpuinfo Hardware string \"%.*s\"",
2347 					(int) hardware_length, hardware);
2348 				return chipset;
2349 			}
2350 		#endif
2351 
2352 		/* Check Spreadtrum SC signature */
2353 		if (match_sc(hardware, hardware_end, &chipset)) {
2354 			cpuinfo_log_debug(
2355 				"matched Spreadtrum SC signature in /proc/cpuinfo Hardware string \"%.*s\"",
2356 				(int) hardware_length, hardware);
2357 			return chipset;
2358 		}
2359 
2360 		#if CPUINFO_ARCH_ARM
2361 			/* Check Marvell PXA signature */
2362 			if (match_pxa(hardware, hardware_end, &chipset)) {
2363 				cpuinfo_log_debug(
2364 					"matched Marvell PXA signature in /proc/cpuinfo Hardware string \"%.*s\"",
2365 					(int) hardware_length, hardware);
2366 				return chipset;
2367 			}
2368 		#endif
2369 
2370 		/* Match /sun\d+i/ signature and map to Allwinner chipset name */
2371 		if (match_and_parse_sunxi(hardware, hardware_end, cores, &chipset)) {
2372 			cpuinfo_log_debug(
2373 				"matched sunxi (Allwinner Ax) signature in /proc/cpuinfo Hardware string \"%.*s\"",
2374 				(int) hardware_length, hardware);
2375 			return chipset;
2376 		}
2377 
2378 		/* Check Broadcom BCM signature */
2379 		if (match_bcm(hardware, hardware_end, &chipset)) {
2380 			cpuinfo_log_debug(
2381 				"matched Broadcom BCM signature in /proc/cpuinfo Hardware string \"%.*s\"",
2382 				(int) hardware_length, hardware);
2383 			return chipset;
2384 		}
2385 
2386 		#if CPUINFO_ARCH_ARM
2387 			/* Check Texas Instruments OMAP signature */
2388 			if (match_omap(hardware, hardware_end, &chipset)) {
2389 				cpuinfo_log_debug(
2390 					"matched Texas Instruments OMAP signature in /proc/cpuinfo Hardware string \"%.*s\"",
2391 					(int) hardware_length, hardware);
2392 				return chipset;
2393 			}
2394 
2395 			/* Check WonderMedia WMT signature and decode chipset from frequency and number of cores  */
2396 			if (match_and_parse_wmt(hardware, hardware_end, cores, max_cpu_freq_max, &chipset)) {
2397 				cpuinfo_log_debug(
2398 					"matched WonderMedia WMT signature in /proc/cpuinfo Hardware string \"%.*s\"",
2399 					(int) hardware_length, hardware);
2400 				return chipset;
2401 			}
2402 
2403 		#endif
2404 
2405 		/* Check Telechips TCC signature */
2406 		if (match_tcc(hardware, hardware_end, &chipset)) {
2407 			cpuinfo_log_debug(
2408 				"matched Telechips TCC signature in /proc/cpuinfo Hardware string \"%.*s\"",
2409 				(int) hardware_length, hardware);
2410 			return chipset;
2411 		}
2412 
2413 		/* Compare to tabulated Hardware values for popular chipsets/devices which can't be otherwise detected */
2414 		for (size_t i = 0; i < CPUINFO_COUNT_OF(special_hardware_map_entries); i++) {
2415 			if (strncmp(special_hardware_map_entries[i].platform, hardware, hardware_length) == 0 &&
2416 					special_hardware_map_entries[i].platform[hardware_length] == 0)
2417 			{
2418 				cpuinfo_log_debug(
2419 					"found /proc/cpuinfo Hardware string \"%.*s\" in special chipset table",
2420 					(int) hardware_length, hardware);
2421 				/* Create chipset name from entry */
2422 				return (struct cpuinfo_arm_chipset) {
2423 					.vendor = chipset_series_vendor[special_hardware_map_entries[i].series],
2424 					.series = (enum cpuinfo_arm_chipset_series) special_hardware_map_entries[i].series,
2425 					.model = special_hardware_map_entries[i].model,
2426 					.suffix = {
2427 						[0] = special_hardware_map_entries[i].suffix,
2428 					},
2429 				};
2430 			}
2431 		}
2432 	}
2433 
2434 	return (struct cpuinfo_arm_chipset) {
2435 		.vendor = cpuinfo_arm_chipset_vendor_unknown,
2436 		.series = cpuinfo_arm_chipset_series_unknown,
2437 	};
2438 }
2439 
2440 #ifdef __ANDROID__
2441 	static const struct special_map_entry special_board_map_entries[] = {
2442 		{
2443 			/* "hi6250" -> HiSilicon Kirin 650 */
2444 			.platform = "hi6250",
2445 			.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
2446 			.model = 650,
2447 		},
2448 		{
2449 			/* "hi6210sft" -> HiSilicon Kirin 620 */
2450 			.platform = "hi6210sft",
2451 			.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
2452 			.model = 620,
2453 		},
2454 #if CPUINFO_ARCH_ARM
2455 		{
2456 			/* "hi3630" -> HiSilicon Kirin 920 */
2457 			.platform = "hi3630",
2458 			.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
2459 			.model = 920,
2460 		},
2461 #endif /* CPUINFO_ARCH_ARM */
2462 		{
2463 			/* "hi3635" -> HiSilicon Kirin 930 */
2464 			.platform = "hi3635",
2465 			.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
2466 			.model = 930,
2467 		},
2468 		{
2469 			/* "hi3650" -> HiSilicon Kirin 950 */
2470 			.platform = "hi3650",
2471 			.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
2472 			.model = 950,
2473 		},
2474 		{
2475 			/* "hi3660" -> HiSilicon Kirin 960 */
2476 			.platform = "hi3660",
2477 			.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
2478 			.model = 960,
2479 		},
2480 #if CPUINFO_ARCH_ARM
2481 		{
2482 			/* "mp523x" -> Renesas MP5232 */
2483 			.platform = "mp523x",
2484 			.series = cpuinfo_arm_chipset_series_renesas_mp,
2485 			.model = 5232,
2486 		},
2487 #endif /* CPUINFO_ARCH_ARM */
2488 		{
2489 			/* "BEETHOVEN" (Huawei MadiaPad M3) -> HiSilicon Kirin 950 */
2490 			.platform = "BEETHOVEN",
2491 			.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
2492 			.model = 950,
2493 		},
2494 #if CPUINFO_ARCH_ARM
2495 		{
2496 			/* "hws7701u" (Huawei MediaPad 7 Youth) -> Rockchip RK3168 */
2497 			.platform = "hws7701u",
2498 			.series = cpuinfo_arm_chipset_series_rockchip_rk,
2499 			.model = 3168,
2500 		},
2501 		{
2502 			/* "g2mv" (LG G2 mini LTE) -> Nvidia Tegra SL460N */
2503 			.platform = "g2mv",
2504 			.series = cpuinfo_arm_chipset_series_nvidia_tegra_sl,
2505 			.model = 460,
2506 			.suffix = 'N',
2507 		},
2508 		{
2509 			/* "K00F" (Asus MeMO Pad 10) -> Rockchip RK3188 */
2510 			.platform = "K00F",
2511 			.series = cpuinfo_arm_chipset_series_rockchip_rk,
2512 			.model = 3188,
2513 		},
2514 		{
2515 			/* "T7H" (HP Slate 7) -> Rockchip RK3066 */
2516 			.platform = "T7H",
2517 			.series = cpuinfo_arm_chipset_series_rockchip_rk,
2518 			.model = 3066,
2519 		},
2520 		{
2521 			/* "tuna" (Samsung Galaxy Nexus) -> Texas Instruments OMAP4460 */
2522 			.platform = "tuna",
2523 			.series = cpuinfo_arm_chipset_series_texas_instruments_omap,
2524 			.model = 4460,
2525 		},
2526 		{
2527 			/* "grouper" (Asus Nexus 7 2012) -> Nvidia Tegra T30L */
2528 			.platform = "grouper",
2529 			.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2530 			.model = 30,
2531 			.suffix = 'L',
2532 		},
2533 #endif /* CPUINFO_ARCH_ARM */
2534 		{
2535 			/* "flounder" (HTC Nexus 9) -> Nvidia Tegra T132 */
2536 			.platform = "flounder",
2537 			.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2538 			.model = 132,
2539 		},
2540 		{
2541 			/* "dragon" (Google Pixel C) -> Nvidia Tegra T210 */
2542 			.platform = "dragon",
2543 			.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2544 			.model = 210,
2545 		},
2546 		{
2547 			/* "sailfish" (Google Pixel) -> Qualcomm MSM8996PRO */
2548 			.platform = "sailfish",
2549 			.series = cpuinfo_arm_chipset_series_qualcomm_msm,
2550 			.model = 8996,
2551 			.suffix = 'P',
2552 		},
2553 		{
2554 			/* "marlin" (Google Pixel XL) -> Qualcomm MSM8996PRO */
2555 			.platform = "marlin",
2556 			.series = cpuinfo_arm_chipset_series_qualcomm_msm,
2557 			.model = 8996,
2558 			.suffix = 'P',
2559 		},
2560 	};
2561 
2562 	/*
2563 	 * Decodes chipset name from ro.product.board Android system property.
2564 	 * For some chipsets, the function relies frequency and on number of cores for chipset detection.
2565 	 *
2566 	 * @param[in] platform - ro.product.board value.
2567 	 * @param cores - number of cores in the chipset.
2568 	 * @param max_cpu_freq_max - maximum of /sys/devices/system/cpu/cpu<number>/cpofreq/cpu_freq_max values.
2569 	 *
2570 	 * @returns Decoded chipset name. If chipset could not be decoded, the resulting structure would use `unknown` vendor
2571 	 *          and series identifiers.
2572 	 */
cpuinfo_arm_android_decode_chipset_from_ro_product_board(const char ro_product_board[restrict static CPUINFO_BUILD_PROP_VALUE_MAX],uint32_t cores,uint32_t max_cpu_freq_max)2573 	struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_product_board(
2574 		const char ro_product_board[restrict static CPUINFO_BUILD_PROP_VALUE_MAX],
2575 		uint32_t cores, uint32_t max_cpu_freq_max)
2576 	{
2577 		struct cpuinfo_arm_chipset chipset;
2578 		const char* board = ro_product_board;
2579 		const size_t board_length = strnlen(ro_product_board, CPUINFO_BUILD_PROP_VALUE_MAX);
2580 		const char* board_end = ro_product_board + board_length;
2581 
2582 		/* Check Qualcomm MSM/APQ signature */
2583 		if (match_msm_apq(board, board_end, &chipset)) {
2584 			cpuinfo_log_debug(
2585 				"matched Qualcomm MSM/APQ signature in ro.product.board string \"%.*s\"", (int) board_length, board);
2586 			return chipset;
2587 		}
2588 
2589 		/* Check universaXXXX (Samsung Exynos) signature */
2590 		if (match_universal(board, board_end, &chipset)) {
2591 			cpuinfo_log_debug(
2592 				"matched UNIVERSAL (Samsung Exynos) signature in ro.product.board string \"%.*s\"",
2593 				(int) board_length, board);
2594 			return chipset;
2595 		}
2596 
2597 		#if CPUINFO_ARCH_ARM
2598 			/* Check SMDK (Samsung Exynos) signature */
2599 			if (match_and_parse_smdk(board, board_end, cores, &chipset)) {
2600 				cpuinfo_log_debug(
2601 					"matched SMDK (Samsung Exynos) signature in ro.product.board string \"%.*s\"",
2602 					(int) board_length, board);
2603 				return chipset;
2604 			}
2605 		#endif
2606 
2607 		/* Check MediaTek MT signature */
2608 		if (match_mt(board, board_end, true, &chipset)) {
2609 			cpuinfo_log_debug(
2610 				"matched MediaTek MT signature in ro.product.board string \"%.*s\"",
2611 				(int) board_length, board);
2612 			return chipset;
2613 		}
2614 
2615 		/* Check Spreadtrum SC signature */
2616 		if (match_sc(board, board_end, &chipset)) {
2617 			cpuinfo_log_debug(
2618 				"matched Spreadtrum SC signature in ro.product.board string \"%.*s\"",
2619 				(int) board_length, board);
2620 			return chipset;
2621 		}
2622 
2623 		#if CPUINFO_ARCH_ARM
2624 			/* Check Marvell PXA signature */
2625 			if (match_pxa(board, board_end, &chipset)) {
2626 				cpuinfo_log_debug(
2627 					"matched Marvell PXA signature in ro.product.board string \"%.*s\"",
2628 					(int) board_length, board);
2629 				return chipset;
2630 			}
2631 
2632 			/* Check Leadcore LCxxxx signature */
2633 			if (match_lc(board, board_end, &chipset)) {
2634 				cpuinfo_log_debug(
2635 					"matched Leadcore LC signature in ro.product.board string \"%.*s\"",
2636 					(int) board_length, board);
2637 				return chipset;
2638 			}
2639 
2640 			/*
2641 			 * Compare to tabulated ro.product.board values for Broadcom chipsets and decode chipset from frequency and
2642 			 * number of cores.
2643 			 */
2644 			if (match_and_parse_broadcom(board, board_end, cores, max_cpu_freq_max, &chipset)) {
2645 				cpuinfo_log_debug(
2646 					"found ro.product.board string \"%.*s\" in Broadcom chipset table",
2647 					(int) board_length, board);
2648 				return chipset;
2649 			}
2650 		#endif
2651 
2652 		/* Compare to tabulated ro.product.board values for Huawei devices which don't report chipset elsewhere */
2653 		if (match_and_parse_huawei(board, board_end, &chipset)) {
2654 			cpuinfo_log_debug(
2655 				"found ro.product.board string \"%.*s\" in Huawei chipset table",
2656 				(int) board_length, board);
2657 			return chipset;
2658 		}
2659 
2660 		/* Compare to tabulated ro.product.board values for popular chipsets/devices which can't be otherwise detected */
2661 		for (size_t i = 0; i < CPUINFO_COUNT_OF(special_board_map_entries); i++) {
2662 			if (strncmp(special_board_map_entries[i].platform, board, board_length) == 0 &&
2663 					special_board_map_entries[i].platform[board_length] == 0)
2664 			{
2665 				cpuinfo_log_debug(
2666 					"found ro.product.board string \"%.*s\" in special chipset table",
2667 					(int) board_length, board);
2668 				/* Create chipset name from entry */
2669 				return (struct cpuinfo_arm_chipset) {
2670 					.vendor = chipset_series_vendor[special_board_map_entries[i].series],
2671 					.series = (enum cpuinfo_arm_chipset_series) special_board_map_entries[i].series,
2672 					.model = special_board_map_entries[i].model,
2673 					.suffix = {
2674 						[0] = special_board_map_entries[i].suffix,
2675 						/* The suffix of MSM8996PRO is truncated at the first letter, reconstruct it here. */
2676 						[1] = special_board_map_entries[i].suffix == 'P' ? 'R' : 0,
2677 						[2] = special_board_map_entries[i].suffix == 'P' ? 'O' : 0,
2678 					},
2679 				};
2680 			}
2681 		}
2682 
2683 		return (struct cpuinfo_arm_chipset) {
2684 			.vendor = cpuinfo_arm_chipset_vendor_unknown,
2685 			.series = cpuinfo_arm_chipset_series_unknown,
2686 		};
2687 	}
2688 
2689 	struct amlogic_map_entry {
2690 		char ro_board_platform[6];
2691 		uint16_t model;
2692 		uint8_t series;
2693 		char suffix[3];
2694 	};
2695 
2696 	static const struct amlogic_map_entry amlogic_map_entries[] = {
2697 #if CPUINFO_ARCH_ARM
2698 		{
2699 			/* "meson3" -> Amlogic AML8726-M */
2700 			.ro_board_platform = "meson3",
2701 			.series = cpuinfo_arm_chipset_series_amlogic_aml,
2702 			.model = 8726,
2703 			.suffix = "-M",
2704 		},
2705 		{
2706 			/* "meson6" -> Amlogic AML8726-MX */
2707 			.ro_board_platform = "meson6",
2708 			.series = cpuinfo_arm_chipset_series_amlogic_aml,
2709 			.model = 8726,
2710 			.suffix = "-MX",
2711 		},
2712 		{
2713 			/* "meson8" -> Amlogic S805 */
2714 			.ro_board_platform = "meson8",
2715 			.series = cpuinfo_arm_chipset_series_amlogic_s,
2716 			.model = 805,
2717 		},
2718 #endif /* CPUINFO_ARCH_ARM */
2719 		{
2720 			/* "gxbaby" -> Amlogic S905 */
2721 			.ro_board_platform = "gxbaby",
2722 			.series = cpuinfo_arm_chipset_series_amlogic_s,
2723 			.model = 905,
2724 		},
2725 		{
2726 			/* "gxl" -> Amlogic S905X */
2727 			.ro_board_platform = "gxl",
2728 			.series = cpuinfo_arm_chipset_series_amlogic_s,
2729 			.model = 905,
2730 			.suffix = "X",
2731 		},
2732 		{
2733 			/* "gxm" -> Amlogic S912 */
2734 			.ro_board_platform = "gxm",
2735 			.series = cpuinfo_arm_chipset_series_amlogic_s,
2736 			.model = 912,
2737 		},
2738 	};
2739 
2740 	static const struct special_map_entry special_platform_map_entries[] = {
2741 #if CPUINFO_ARCH_ARM
2742 		{
2743 			/* "hi6620oem" -> HiSilicon Kirin 910T */
2744 			.platform = "hi6620oem",
2745 			.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
2746 			.model = 910,
2747 			.suffix = 'T',
2748 		},
2749 #endif /* CPUINFO_ARCH_ARM */
2750 		{
2751 			/* "hi6250" -> HiSilicon Kirin 650 */
2752 			.platform = "hi6250",
2753 			.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
2754 			.model = 650,
2755 		},
2756 		{
2757 			/* "hi6210sft" -> HiSilicon Kirin 620 */
2758 			.platform = "hi6210sft",
2759 			.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
2760 			.model = 620,
2761 		},
2762 #if CPUINFO_ARCH_ARM
2763 		{
2764 			/* "hi3630" -> HiSilicon Kirin 920 */
2765 			.platform = "hi3630",
2766 			.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
2767 			.model = 920,
2768 		},
2769 #endif /* CPUINFO_ARCH_ARM */
2770 		{
2771 			/* "hi3635" -> HiSilicon Kirin 930 */
2772 			.platform = "hi3635",
2773 			.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
2774 			.model = 930,
2775 		},
2776 		{
2777 			/* "hi3650" -> HiSilicon Kirin 950 */
2778 			.platform = "hi3650",
2779 			.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
2780 			.model = 950,
2781 		},
2782 		{
2783 			/* "hi3660" -> HiSilicon Kirin 960 */
2784 			.platform = "hi3660",
2785 			.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
2786 			.model = 960,
2787 		},
2788 #if CPUINFO_ARCH_ARM
2789 		{
2790 			/* "k3v2oem1" -> HiSilicon K3V2 */
2791 			.platform = "k3v2oem1",
2792 			.series = cpuinfo_arm_chipset_series_hisilicon_k3v,
2793 			.model = 2,
2794 		},
2795 		{
2796 			/* "k3v200" -> HiSilicon K3V2 */
2797 			.platform = "k3v200",
2798 			.series = cpuinfo_arm_chipset_series_hisilicon_k3v,
2799 			.model = 2,
2800 		},
2801 		{
2802 			/* "montblanc" -> NovaThor U8500 */
2803 			.platform = "montblanc",
2804 			.series = cpuinfo_arm_chipset_series_novathor_u,
2805 			.model = 8500,
2806 		},
2807 #endif /* CPUINFO_ARCH_ARM */
2808 		{
2809 			/* "song" -> Pinecone Surge S1 */
2810 			.platform = "song",
2811 			.series = cpuinfo_arm_chipset_series_pinecone_surge_s,
2812 			.model = 1,
2813 		},
2814 #if CPUINFO_ARCH_ARM
2815 		{
2816 			/* "rk322x" -> RockChip RK3229 */
2817 			.platform = "rk322x",
2818 			.series = cpuinfo_arm_chipset_series_rockchip_rk,
2819 			.model = 3229,
2820 		},
2821 #endif /* CPUINFO_ARCH_ARM */
2822 		{
2823 			/* "tegra132" -> Nvidia Tegra T132 */
2824 			.platform = "tegra132",
2825 			.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2826 			.model = 132,
2827 		},
2828 		{
2829 			/* "tegra210_dragon" -> Nvidia Tegra T210 */
2830 			.platform = "tegra210_dragon",
2831 			.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2832 			.model = 210,
2833 		},
2834 #if CPUINFO_ARCH_ARM
2835 		{
2836 			/* "tegra4" -> Nvidia Tegra T114 */
2837 			.platform = "tegra4",
2838 			.series = cpuinfo_arm_chipset_series_nvidia_tegra_t,
2839 			.model = 114,
2840 		},
2841 		{
2842 			/* "s5pc110" -> Samsung Exynos 3110 */
2843 			.platform = "s5pc110",
2844 			.series = cpuinfo_arm_chipset_series_samsung_exynos,
2845 			.model = 3110,
2846 		},
2847 #endif /* CPUINFO_ARCH_ARM */
2848 	};
2849 
2850 	/*
2851 	 * Decodes chipset name from ro.board.platform Android system property.
2852 	 * For some chipsets, the function relies frequency and on number of cores for chipset detection.
2853 	 *
2854 	 * @param[in] platform - ro.board.platform value.
2855 	 * @param cores - number of cores in the chipset.
2856 	 * @param max_cpu_freq_max - maximum of /sys/devices/system/cpu/cpu<number>/cpofreq/cpu_freq_max values.
2857 	 *
2858 	 * @returns Decoded chipset name. If chipset could not be decoded, the resulting structure would use `unknown` vendor
2859 	 *          and series identifiers.
2860 	 */
cpuinfo_arm_android_decode_chipset_from_ro_board_platform(const char platform[restrict static CPUINFO_BUILD_PROP_VALUE_MAX],uint32_t cores,uint32_t max_cpu_freq_max)2861 	struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_board_platform(
2862 		const char platform[restrict static CPUINFO_BUILD_PROP_VALUE_MAX],
2863 		uint32_t cores, uint32_t max_cpu_freq_max)
2864 	{
2865 		struct cpuinfo_arm_chipset chipset;
2866 		const size_t platform_length = strnlen(platform, CPUINFO_BUILD_PROP_VALUE_MAX);
2867 		const char* platform_end = platform + platform_length;
2868 
2869 		/* Check Qualcomm MSM/APQ signature */
2870 		if (match_msm_apq(platform, platform_end, &chipset)) {
2871 			cpuinfo_log_debug(
2872 				"matched Qualcomm MSM/APQ signature in ro.board.platform string \"%.*s\"",
2873 				(int) platform_length, platform);
2874 			return chipset;
2875 		}
2876 
2877 		/* Check exynosXXXX (Samsung Exynos) signature */
2878 		if (match_exynos(platform, platform_end, &chipset)) {
2879 			cpuinfo_log_debug(
2880 				"matched exynosXXXX (Samsung Exynos) signature in ro.board.platform string \"%.*s\"",
2881 				(int) platform_length, platform);
2882 			return chipset;
2883 		}
2884 
2885 		/* Check MediaTek MT signature */
2886 		if (match_mt(platform, platform_end, true, &chipset)) {
2887 			cpuinfo_log_debug(
2888 				"matched MediaTek MT signature in ro.board.platform string \"%.*s\"", (int) platform_length, platform);
2889 			return chipset;
2890 		}
2891 
2892 		/* Check HiSilicon Kirin signature */
2893 		if (match_kirin(platform, platform_end, &chipset)) {
2894 			cpuinfo_log_debug(
2895 				"matched HiSilicon Kirin signature in ro.board.platform string \"%.*s\"", (int) platform_length, platform);
2896 			return chipset;
2897 		}
2898 
2899 		/* Check Spreadtrum SC signature */
2900 		if (match_sc(platform, platform_end, &chipset)) {
2901 			cpuinfo_log_debug(
2902 				"matched Spreadtrum SC signature in ro.board.platform string \"%.*s\"", (int) platform_length, platform);
2903 			return chipset;
2904 		}
2905 
2906 		/* Check Rockchip RK signature */
2907 		if (match_rk(platform, platform_end, &chipset)) {
2908 			cpuinfo_log_debug(
2909 				"matched Rockchip RK signature in ro.board.platform string \"%.*s\"", (int) platform_length, platform);
2910 			return chipset;
2911 		}
2912 
2913 		#if CPUINFO_ARCH_ARM
2914 			/* Check Leadcore LCxxxx signature */
2915 			if (match_lc(platform, platform_end, &chipset)) {
2916 				cpuinfo_log_debug(
2917 					"matched Leadcore LC signature in ro.board.platform string \"%.*s\"", (int) platform_length, platform);
2918 				return chipset;
2919 			}
2920 		#endif
2921 
2922 		/* Compare to tabulated ro.board.platform values for Huawei devices which don't report chipset elsewhere */
2923 		if (match_and_parse_huawei(platform, platform_end, &chipset)) {
2924 			cpuinfo_log_debug(
2925 				"found ro.board.platform string \"%.*s\" in Huawei chipset table",
2926 				(int) platform_length, platform);
2927 			return chipset;
2928 		}
2929 
2930 		#if CPUINFO_ARCH_ARM
2931 			/*
2932 			 * Compare to known ro.board.platform values for Broadcom devices and
2933 			 * detect chipset from frequency and number of cores
2934 			 */
2935 			if (match_and_parse_broadcom(platform, platform_end, cores, max_cpu_freq_max, &chipset)) {
2936 				cpuinfo_log_debug(
2937 					"found ro.board.platform string \"%.*s\" in Broadcom chipset table",
2938 					(int) platform_length, platform);
2939 				return chipset;
2940 			}
2941 
2942 			/*
2943 			 * Compare to ro.board.platform value ("omap4") for OMAP4xxx chipsets.
2944 			 * Upon successful match, detect OMAP4430 from frequency and number of cores.
2945 			 */
2946 			if (platform_length == 5 && cores == 2 && max_cpu_freq_max == 1008000 && memcmp(platform, "omap4", 5) == 0) {
2947 				cpuinfo_log_debug(
2948 					"matched Texas Instruments OMAP4 signature in ro.board.platform string \"%.*s\"",
2949 					(int) platform_length, platform);
2950 
2951 				return (struct cpuinfo_arm_chipset) {
2952 					.vendor = cpuinfo_arm_chipset_vendor_texas_instruments,
2953 					.series = cpuinfo_arm_chipset_series_texas_instruments_omap,
2954 					.model = 4430,
2955 				};
2956 			}
2957 		#endif
2958 
2959 		/*
2960 		 * Compare to tabulated ro.board.platform values for Amlogic chipsets/devices which can't be otherwise detected.
2961 		 * The tabulated Amlogic ro.board.platform values have not more than 6 characters.
2962 		 */
2963 		if (platform_length <= 6) {
2964 			for (size_t i = 0; i < CPUINFO_COUNT_OF(amlogic_map_entries); i++) {
2965 				if (strncmp(amlogic_map_entries[i].ro_board_platform, platform, 6) == 0) {
2966 					cpuinfo_log_debug(
2967 						"found ro.board.platform string \"%.*s\" in Amlogic chipset table",
2968 						(int) platform_length, platform);
2969 					/* Create chipset name from entry */
2970 					return (struct cpuinfo_arm_chipset) {
2971 						.vendor = cpuinfo_arm_chipset_vendor_amlogic,
2972 						.series = (enum cpuinfo_arm_chipset_series) amlogic_map_entries[i].series,
2973 						.model = amlogic_map_entries[i].model,
2974 						.suffix = {
2975 							[0] = amlogic_map_entries[i].suffix[0],
2976 							[1] = amlogic_map_entries[i].suffix[1],
2977 							[2] = amlogic_map_entries[i].suffix[2],
2978 						},
2979 					};
2980 				}
2981 			}
2982 		}
2983 
2984 		/* Compare to tabulated ro.board.platform values for popular chipsets/devices which can't be otherwise detected */
2985 		for (size_t i = 0; i < CPUINFO_COUNT_OF(special_platform_map_entries); i++) {
2986 			if (strncmp(special_platform_map_entries[i].platform, platform, platform_length) == 0 &&
2987 					special_platform_map_entries[i].platform[platform_length] == 0)
2988 			{
2989 				/* Create chipset name from entry */
2990 				cpuinfo_log_debug(
2991 					"found ro.board.platform string \"%.*s\" in special chipset table", (int) platform_length, platform);
2992 				return (struct cpuinfo_arm_chipset) {
2993 					.vendor = chipset_series_vendor[special_platform_map_entries[i].series],
2994 					.series = (enum cpuinfo_arm_chipset_series) special_platform_map_entries[i].series,
2995 					.model = special_platform_map_entries[i].model,
2996 					.suffix = {
2997 						[0] = special_platform_map_entries[i].suffix,
2998 					},
2999 				};
3000 			}
3001 		}
3002 
3003 		/* None of the ro.board.platform signatures matched, indicate unknown chipset */
3004 		return (struct cpuinfo_arm_chipset) {
3005 			.vendor = cpuinfo_arm_chipset_vendor_unknown,
3006 			.series = cpuinfo_arm_chipset_series_unknown,
3007 		};
3008 	}
3009 
3010 	/*
3011 	 * Decodes chipset name from ro.mediatek.platform Android system property.
3012 	 *
3013 	 * @param[in] platform - ro.mediatek.platform value.
3014 	 *
3015 	 * @returns Decoded chipset name. If chipset could not be decoded, the resulting structure would use `unknown`
3016 	 *          vendor and series identifiers.
3017 	 */
cpuinfo_arm_android_decode_chipset_from_ro_mediatek_platform(const char platform[restrict static CPUINFO_BUILD_PROP_VALUE_MAX])3018 	struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_mediatek_platform(
3019 		const char platform[restrict static CPUINFO_BUILD_PROP_VALUE_MAX])
3020 	{
3021 		struct cpuinfo_arm_chipset chipset;
3022 		const char* platform_end = platform + strnlen(platform, CPUINFO_BUILD_PROP_VALUE_MAX);
3023 
3024 		/* Check MediaTek MT signature */
3025 		if (match_mt(platform, platform_end, false, &chipset)) {
3026 			return chipset;
3027 		}
3028 
3029 		return (struct cpuinfo_arm_chipset) {
3030 			.vendor = cpuinfo_arm_chipset_vendor_unknown,
3031 			.series = cpuinfo_arm_chipset_series_unknown,
3032 		};
3033 	}
3034 
3035 
3036 	/*
3037 	 * Decodes chipset name from ro.arch Android system property.
3038 	 *
3039 	 * The ro.arch property is matched only against Samsung Exynos signature. Systems with other chipset rarely
3040 	 * configure ro.arch Android system property, and can be decoded through other properties, but some Exynos
3041 	 * chipsets are identified only in ro.arch.
3042 	 *
3043 	 * @param[in] arch - ro.arch value.
3044 	 *
3045 	 * @returns Decoded chipset name. If chipset could not be decoded, the resulting structure would use `unknown`
3046 	 *          vendor and series identifiers.
3047 	 */
cpuinfo_arm_android_decode_chipset_from_ro_arch(const char arch[restrict static CPUINFO_BUILD_PROP_VALUE_MAX])3048 	struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_arch(
3049 		const char arch[restrict static CPUINFO_BUILD_PROP_VALUE_MAX])
3050 	{
3051 		struct cpuinfo_arm_chipset chipset;
3052 		const char* arch_end = arch + strnlen(arch, CPUINFO_BUILD_PROP_VALUE_MAX);
3053 
3054 		/* Check Samsung exynosXXXX signature */
3055 		if (match_exynos(arch, arch_end, &chipset)) {
3056 			return chipset;
3057 		}
3058 
3059 		return (struct cpuinfo_arm_chipset) {
3060 			.vendor = cpuinfo_arm_chipset_vendor_unknown,
3061 			.series = cpuinfo_arm_chipset_series_unknown,
3062 		};
3063 	}
3064 
3065 	/*
3066 	 * Decodes chipset name from ro.chipname or ro.hardware.chipname Android system property.
3067 	 *
3068 	 * @param[in] chipname - ro.chipname or ro.hardware.chipname value.
3069 	 *
3070 	 * @returns Decoded chipset name. If chipset could not be decoded, the resulting structure would use `unknown` vendor
3071 	 *          and series identifiers.
3072 	 */
3073 
cpuinfo_arm_android_decode_chipset_from_ro_chipname(const char chipname[restrict static CPUINFO_BUILD_PROP_VALUE_MAX])3074 	struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_chipname(
3075 		const char chipname[restrict static CPUINFO_BUILD_PROP_VALUE_MAX])
3076 	{
3077 		struct cpuinfo_arm_chipset chipset;
3078 		const size_t chipname_length = strnlen(chipname, CPUINFO_BUILD_PROP_VALUE_MAX);
3079 		const char* chipname_end = chipname + chipname_length;
3080 
3081 		/* Check Qualcomm MSM/APQ signatures */
3082 		if (match_msm_apq(chipname, chipname_end, &chipset)) {
3083 			cpuinfo_log_debug(
3084 				"matched Qualcomm MSM/APQ signature in ro.chipname string \"%.*s\"",
3085 				(int) chipname_length, chipname);
3086 			return chipset;
3087 		}
3088 
3089 		/* Check SMxxxx (Qualcomm Snapdragon) signature */
3090 		if (match_sm(chipname, chipname_end, &chipset)) {
3091 			cpuinfo_log_debug(
3092 				"matched Qualcomm SM signature in /proc/cpuinfo Hardware string \"%.*s\"",
3093 				(int) chipname_length, chipname);
3094 			return chipset;
3095 		}
3096 
3097 		/* Check exynosXXXX (Samsung Exynos) signature */
3098 		if (match_exynos(chipname, chipname_end, &chipset)) {
3099 			cpuinfo_log_debug(
3100 				"matched exynosXXXX (Samsung Exynos) signature in ro.chipname string \"%.*s\"",
3101 				(int) chipname_length, chipname);
3102 			return chipset;
3103 		}
3104 
3105 		/* Check universalXXXX (Samsung Exynos) signature */
3106 		if (match_universal(chipname, chipname_end, &chipset)) {
3107 			cpuinfo_log_debug(
3108 				"matched UNIVERSAL (Samsung Exynos) signature in ro.chipname Hardware string \"%.*s\"",
3109 				(int) chipname_length, chipname);
3110 			return chipset;
3111 		}
3112 
3113 		/* Check MediaTek MT signature */
3114 		if (match_mt(chipname, chipname_end, true, &chipset)) {
3115 			cpuinfo_log_debug(
3116 				"matched MediaTek MT signature in ro.chipname string \"%.*s\"",
3117 				(int) chipname_length, chipname);
3118 			return chipset;
3119 		}
3120 
3121 		/* Check Spreadtrum SC signature */
3122 		if (match_sc(chipname, chipname_end, &chipset)) {
3123 			cpuinfo_log_debug(
3124 				"matched Spreadtrum SC signature in ro.chipname string \"%.*s\"",
3125 				(int) chipname_length, chipname);
3126 			return chipset;
3127 		}
3128 
3129 		#if CPUINFO_ARCH_ARM
3130 			/* Check Marvell PXA signature */
3131 			if (match_pxa(chipname, chipname_end, &chipset)) {
3132 				cpuinfo_log_debug(
3133 					"matched Marvell PXA signature in ro.chipname string \"%.*s\"",
3134 					(int) chipname_length, chipname);
3135 				return chipset;
3136 			}
3137 
3138 			/* Compare to ro.chipname value ("mp523x") for Renesas MP5232 which can't be otherwise detected */
3139 			if (chipname_length == 6 && memcmp(chipname, "mp523x", 6) == 0) {
3140 				cpuinfo_log_debug(
3141 					"matched Renesas MP5232 signature in ro.chipname string \"%.*s\"",
3142 					(int) chipname_length, chipname);
3143 
3144 				return (struct cpuinfo_arm_chipset) {
3145 					.vendor = cpuinfo_arm_chipset_vendor_renesas,
3146 					.series = cpuinfo_arm_chipset_series_renesas_mp,
3147 					.model = 5232,
3148 				};
3149 			}
3150 		#endif
3151 
3152 		return (struct cpuinfo_arm_chipset) {
3153 			.vendor = cpuinfo_arm_chipset_vendor_unknown,
3154 			.series = cpuinfo_arm_chipset_series_unknown,
3155 		};
3156 	}
3157 #endif /* __ANDROID__ */
3158 
3159 /*
3160  * Fix common bugs, typos, and renames in chipset name.
3161  *
3162  * @param[in,out] chipset - chipset name to fix.
3163  * @param cores - number of cores in the chipset.
3164  * @param max_cpu_freq_max - maximum of /sys/devices/system/cpu/cpu<number>/cpofreq/cpu_freq_max values.
3165  */
cpuinfo_arm_fixup_chipset(struct cpuinfo_arm_chipset chipset[restrict static1],uint32_t cores,uint32_t max_cpu_freq_max)3166 void cpuinfo_arm_fixup_chipset(
3167 	struct cpuinfo_arm_chipset chipset[restrict static 1], uint32_t cores, uint32_t max_cpu_freq_max)
3168 {
3169 	switch (chipset->series) {
3170 		case cpuinfo_arm_chipset_series_qualcomm_msm:
3171 			/* Check if there is suffix */
3172 			if (chipset->suffix[0] == 0) {
3173 				/* No suffix, but the model may be misreported */
3174 				switch (chipset->model) {
3175 					case 8216:
3176 						/* MSM8216 was renamed to MSM8916 */
3177 						cpuinfo_log_info("reinterpreted MSM8216 chipset as MSM8916");
3178 						chipset->model = 8916;
3179 						break;
3180 					case 8916:
3181 						/* Common bug: MSM8939 (Octa-core) reported as MSM8916 (Quad-core) */
3182 						switch (cores) {
3183 							case 4:
3184 								break;
3185 							case 8:
3186 								cpuinfo_log_info("reinterpreted MSM8916 chipset with 8 cores as MSM8939");
3187 								chipset->model = 8939;
3188 								break;
3189 							default:
3190 								cpuinfo_log_warning("system reported invalid %"PRIu32"-core MSM%"PRIu32" chipset",
3191 									cores, chipset->model);
3192 								chipset->model = 0;
3193 						}
3194 						break;
3195 					case 8937:
3196 						/* Common bug: MSM8917 (Quad-core) reported as MSM8937 (Octa-core) */
3197 						switch (cores) {
3198 							case 4:
3199 								cpuinfo_log_info("reinterpreted MSM8937 chipset with 4 cores as MSM8917");
3200 								chipset->model = 8917;
3201 								break;
3202 							case 8:
3203 								break;
3204 							default:
3205 								cpuinfo_log_warning("system reported invalid %"PRIu32"-core MSM%"PRIu32" chipset",
3206 									cores, chipset->model);
3207 								chipset->model = 0;
3208 						}
3209 						break;
3210 					case 8960:
3211 						/* Common bug: APQ8064 (Quad-core) reported as MSM8960 (Dual-core) */
3212 						switch (cores) {
3213 							case 2:
3214 								break;
3215 							case 4:
3216 								cpuinfo_log_info("reinterpreted MSM8960 chipset with 4 cores as APQ8064");
3217 								chipset->series = cpuinfo_arm_chipset_series_qualcomm_apq;
3218 								chipset->model = 8064;
3219 								break;
3220 							default:
3221 								cpuinfo_log_warning("system reported invalid %"PRIu32"-core MSM%"PRIu32" chipset",
3222 									cores, chipset->model);
3223 								chipset->model = 0;
3224 						}
3225 						break;
3226 					case 8996:
3227 						/* Common bug: MSM8994 (Octa-core) reported as MSM8996 (Quad-core) */
3228 						switch (cores) {
3229 							case 4:
3230 								break;
3231 							case 8:
3232 								cpuinfo_log_info("reinterpreted MSM8996 chipset with 8 cores as MSM8994");
3233 								chipset->model = 8994;
3234 								break;
3235 							default:
3236 								cpuinfo_log_warning("system reported invalid %"PRIu32"-core MSM%"PRIu32" chipset",
3237 									cores, chipset->model);
3238 								chipset->model = 0;
3239 						}
3240 						break;
3241 #if CPUINFO_ARCH_ARM
3242 					case 8610:
3243 						/* Common bug: MSM8612 (Quad-core) reported as MSM8610 (Dual-core) */
3244 						switch (cores) {
3245 							case 2:
3246 								break;
3247 							case 4:
3248 								cpuinfo_log_info("reinterpreted MSM8610 chipset with 4 cores as MSM8612");
3249 								chipset->model = 8612;
3250 								break;
3251 							default:
3252 								cpuinfo_log_warning("system reported invalid %"PRIu32"-core MSM%"PRIu32" chipset",
3253 									cores, chipset->model);
3254 								chipset->model = 0;
3255 						}
3256 						break;
3257 #endif /* CPUINFO_ARCH_ARM */
3258 				}
3259 			} else {
3260 				/* Suffix may need correction */
3261 				const uint32_t suffix_word = load_u32le(chipset->suffix);
3262 				if (suffix_word == UINT32_C(0x004D534D) /* "\0MSM" = reverse("MSM\0") */) {
3263 					/*
3264 					 * Common bug: model name repeated twice, e.g. "MSM8916MSM8916"
3265 					 * In this case, model matching code parses the second "MSM" as a suffix
3266 					 */
3267 					chipset->suffix[0] = 0;
3268 					chipset->suffix[1] = 0;
3269 					chipset->suffix[2] = 0;
3270 				} else {
3271 					switch (chipset->model) {
3272 						case 8976:
3273 							/* MSM8976SG -> MSM8976PRO */
3274 							if (suffix_word == UINT32_C(0x00004753) /* "\0\0GS" = reverse("SG\0\0") */ ) {
3275 								chipset->suffix[0] = 'P';
3276 								chipset->suffix[1] = 'R';
3277 								chipset->suffix[2] = 'O';
3278 							}
3279 							break;
3280 						case 8996:
3281 							/* MSM8996PRO -> MSM8996PRO-AB or MSM8996PRO-AC */
3282 							if (suffix_word == UINT32_C(0x004F5250) /* "\0ORP" = reverse("PRO\0") */ ) {
3283 								chipset->suffix[3] = '-';
3284 								chipset->suffix[4] = 'A';
3285 								chipset->suffix[5] = 'B' + (char) (max_cpu_freq_max >= 2188800);
3286 							}
3287 							break;
3288 					}
3289 				}
3290 			}
3291 			break;
3292 		case cpuinfo_arm_chipset_series_qualcomm_apq:
3293 		{
3294 			/* Suffix may need correction */
3295 			const uint32_t expected_apq = load_u32le(chipset->suffix);
3296 			if (expected_apq == UINT32_C(0x00515041) /* "\0QPA" = reverse("APQ\0") */) {
3297 				/*
3298 				 * Common bug: model name repeated twice, e.g. "APQ8016APQ8016"
3299 				 * In this case, model matching code parses the second "APQ" as a suffix
3300 				 */
3301 				chipset->suffix[0] = 0;
3302 				chipset->suffix[1] = 0;
3303 				chipset->suffix[2] = 0;
3304 			}
3305 			break;
3306 		}
3307 		case cpuinfo_arm_chipset_series_samsung_exynos:
3308 			switch (chipset->model) {
3309 #if CPUINFO_ARCH_ARM
3310 				case 4410:
3311 					/* Exynos 4410 was renamed to Exynos 4412 */
3312 					chipset->model = 4412;
3313 					break;
3314 				case 5420:
3315 					/* Common bug: Exynos 5260 (Hexa-core) reported as Exynos 5420 (Quad-core) */
3316 					switch (cores) {
3317 						case 4:
3318 							break;
3319 						case 6:
3320 							cpuinfo_log_info("reinterpreted Exynos 5420 chipset with 6 cores as Exynos 5260");
3321 							chipset->model = 5260;
3322 							break;
3323 						default:
3324 							cpuinfo_log_warning("system reported invalid %"PRIu32"-core Exynos 5420 chipset", cores);
3325 							chipset->model = 0;
3326 					}
3327 					break;
3328 #endif /* CPUINFO_ARCH_ARM */
3329 				case 7580:
3330 					/* Common bug: Exynos 7578 (Quad-core) reported as Exynos 7580 (Octa-core) */
3331 					switch (cores) {
3332 						case 4:
3333 							cpuinfo_log_info("reinterpreted Exynos 7580 chipset with 4 cores as Exynos 7578");
3334 							chipset->model = 7578;
3335 							break;
3336 						case 8:
3337 							break;
3338 						default:
3339 							cpuinfo_log_warning("system reported invalid %"PRIu32"-core Exynos 7580 chipset", cores);
3340 							chipset->model = 0;
3341 					}
3342 					break;
3343 			}
3344 			break;
3345 		case cpuinfo_arm_chipset_series_mediatek_mt:
3346 			if (chipset->model == 6752) {
3347 				/* Common bug: MT6732 (Quad-core) reported as MT6752 (Octa-core) */
3348 				switch (cores) {
3349 					case 4:
3350 						cpuinfo_log_info("reinterpreted MT6752 chipset with 4 cores as MT6732");
3351 						chipset->model = 6732;
3352 						break;
3353 					case 8:
3354 						break;
3355 					default:
3356 						cpuinfo_log_warning("system reported invalid %"PRIu32"-core MT6752 chipset", cores);
3357 						chipset->model = 0;
3358 				}
3359 			}
3360 			if (chipset->suffix[0] == 'T') {
3361 				/* Normalization: "TURBO" and "TRUBO" (apparently a typo) -> "T" */
3362 				const uint32_t suffix_word = load_u32le(chipset->suffix + 1);
3363 				switch (suffix_word) {
3364 					case UINT32_C(0x4F425255): /* "OBRU" = reverse("URBO") */
3365 					case UINT32_C(0x4F425552): /* "OBUR" = reverse("RUBO") */
3366 						if (chipset->suffix[5] == 0) {
3367 							chipset->suffix[1] = 0;
3368 							chipset->suffix[2] = 0;
3369 							chipset->suffix[3] = 0;
3370 							chipset->suffix[4] = 0;
3371 						}
3372 						break;
3373 				}
3374 			}
3375 			break;
3376 		case cpuinfo_arm_chipset_series_rockchip_rk:
3377 			if (chipset->model == 3288) {
3378 				/* Common bug: Rockchip RK3399 (Hexa-core) always reported as RK3288 (Quad-core) */
3379 				switch (cores) {
3380 					case 4:
3381 						break;
3382 					case 6:
3383 						cpuinfo_log_info("reinterpreted RK3288 chipset with 6 cores as RK3399");
3384 						chipset->model = 3399;
3385 						break;
3386 					default:
3387 						cpuinfo_log_warning("system reported invalid %"PRIu32"-core RK3288 chipset", cores);
3388 						chipset->model = 0;
3389 				}
3390 			}
3391 			break;
3392 		default:
3393 			break;
3394 	}
3395 }
3396 
3397 /* Map from ARM chipset vendor ID to its string representation */
3398 static const char* chipset_vendor_string[cpuinfo_arm_chipset_vendor_max] = {
3399 	[cpuinfo_arm_chipset_vendor_unknown]           = "Unknown",
3400 	[cpuinfo_arm_chipset_vendor_qualcomm]          = "Qualcomm",
3401 	[cpuinfo_arm_chipset_vendor_mediatek]          = "MediaTek",
3402 	[cpuinfo_arm_chipset_vendor_samsung]           = "Samsung",
3403 	[cpuinfo_arm_chipset_vendor_hisilicon]         = "HiSilicon",
3404 	[cpuinfo_arm_chipset_vendor_actions]           = "Actions",
3405 	[cpuinfo_arm_chipset_vendor_allwinner]         = "Allwinner",
3406 	[cpuinfo_arm_chipset_vendor_amlogic]           = "Amlogic",
3407 	[cpuinfo_arm_chipset_vendor_broadcom]          = "Broadcom",
3408 	[cpuinfo_arm_chipset_vendor_lg]                = "LG",
3409 	[cpuinfo_arm_chipset_vendor_leadcore]          = "Leadcore",
3410 	[cpuinfo_arm_chipset_vendor_marvell]           = "Marvell",
3411 	[cpuinfo_arm_chipset_vendor_mstar]             = "MStar",
3412 	[cpuinfo_arm_chipset_vendor_novathor]          = "NovaThor",
3413 	[cpuinfo_arm_chipset_vendor_nvidia]            = "Nvidia",
3414 	[cpuinfo_arm_chipset_vendor_pinecone]          = "Pinecone",
3415 	[cpuinfo_arm_chipset_vendor_renesas]           = "Renesas",
3416 	[cpuinfo_arm_chipset_vendor_rockchip]          = "Rockchip",
3417 	[cpuinfo_arm_chipset_vendor_spreadtrum]        = "Spreadtrum",
3418 	[cpuinfo_arm_chipset_vendor_telechips]         = "Telechips",
3419 	[cpuinfo_arm_chipset_vendor_texas_instruments] = "Texas Instruments",
3420 	[cpuinfo_arm_chipset_vendor_wondermedia]       = "WonderMedia",
3421 };
3422 
3423 /* Map from ARM chipset series ID to its string representation */
3424 static const char* chipset_series_string[cpuinfo_arm_chipset_series_max] = {
3425 	[cpuinfo_arm_chipset_series_unknown]                = NULL,
3426 	[cpuinfo_arm_chipset_series_qualcomm_qsd]           = "QSD",
3427 	[cpuinfo_arm_chipset_series_qualcomm_msm]           = "MSM",
3428 	[cpuinfo_arm_chipset_series_qualcomm_apq]           = "APQ",
3429 	[cpuinfo_arm_chipset_series_qualcomm_snapdragon]    = "Snapdragon ",
3430 	[cpuinfo_arm_chipset_series_mediatek_mt]            = "MT",
3431 	[cpuinfo_arm_chipset_series_samsung_exynos]         = "Exynos ",
3432 	[cpuinfo_arm_chipset_series_hisilicon_k3v]          = "K3V",
3433 	[cpuinfo_arm_chipset_series_hisilicon_hi]           = "Hi",
3434 	[cpuinfo_arm_chipset_series_hisilicon_kirin]        = "Kirin ",
3435 	[cpuinfo_arm_chipset_series_actions_atm]            = "ATM",
3436 	[cpuinfo_arm_chipset_series_allwinner_a]            = "A",
3437 	[cpuinfo_arm_chipset_series_amlogic_aml]            = "AML",
3438 	[cpuinfo_arm_chipset_series_amlogic_s]              = "S",
3439 	[cpuinfo_arm_chipset_series_broadcom_bcm]           = "BCM",
3440 	[cpuinfo_arm_chipset_series_lg_nuclun]              = "Nuclun ",
3441 	[cpuinfo_arm_chipset_series_leadcore_lc]            = "LC",
3442 	[cpuinfo_arm_chipset_series_marvell_pxa]            = "PXA",
3443 	[cpuinfo_arm_chipset_series_mstar_6a]               = "6A",
3444 	[cpuinfo_arm_chipset_series_novathor_u]             = "U",
3445 	[cpuinfo_arm_chipset_series_nvidia_tegra_t]         = "Tegra T",
3446 	[cpuinfo_arm_chipset_series_nvidia_tegra_ap]        = "Tegra AP",
3447 	[cpuinfo_arm_chipset_series_nvidia_tegra_sl]        = "Tegra SL",
3448 	[cpuinfo_arm_chipset_series_pinecone_surge_s]       = "Surge S",
3449 	[cpuinfo_arm_chipset_series_renesas_mp]             = "MP",
3450 	[cpuinfo_arm_chipset_series_rockchip_rk]            = "RK",
3451 	[cpuinfo_arm_chipset_series_spreadtrum_sc]          = "SC",
3452 	[cpuinfo_arm_chipset_series_telechips_tcc]          = "TCC",
3453 	[cpuinfo_arm_chipset_series_texas_instruments_omap] = "OMAP",
3454 	[cpuinfo_arm_chipset_series_wondermedia_wm]         = "WM",
3455 };
3456 
3457 /* Convert chipset name represented by cpuinfo_arm_chipset structure to a string representation */
cpuinfo_arm_chipset_to_string(const struct cpuinfo_arm_chipset chipset[restrict static1],char name[restrict static CPUINFO_ARM_CHIPSET_NAME_MAX])3458 void cpuinfo_arm_chipset_to_string(
3459 	const struct cpuinfo_arm_chipset chipset[restrict static 1],
3460 	char name[restrict static CPUINFO_ARM_CHIPSET_NAME_MAX])
3461 {
3462 	enum cpuinfo_arm_chipset_vendor vendor = chipset->vendor;
3463 	if (vendor >= cpuinfo_arm_chipset_vendor_max) {
3464 		vendor = cpuinfo_arm_chipset_vendor_unknown;
3465 	}
3466 	enum cpuinfo_arm_chipset_series series = chipset->series;
3467 	if (series >= cpuinfo_arm_chipset_series_max) {
3468 		series = cpuinfo_arm_chipset_series_unknown;
3469 	}
3470 	const char* vendor_string = chipset_vendor_string[vendor];
3471 	const char* series_string = chipset_series_string[series];
3472 	const uint32_t model = chipset->model;
3473 	if (model == 0) {
3474 		if (series == cpuinfo_arm_chipset_series_unknown) {
3475 			strncpy(name, vendor_string, CPUINFO_ARM_CHIPSET_NAME_MAX);
3476 		} else {
3477 			snprintf(name, CPUINFO_ARM_CHIPSET_NAME_MAX,
3478 				"%s %s", vendor_string, series_string);
3479 		}
3480 	} else {
3481 		const size_t suffix_length = strnlen(chipset->suffix, CPUINFO_ARM_CHIPSET_SUFFIX_MAX);
3482 		snprintf(name, CPUINFO_ARM_CHIPSET_NAME_MAX,
3483 			"%s %s%"PRIu32"%.*s", vendor_string, series_string, model, (int) suffix_length, chipset->suffix);
3484 	}
3485 }
3486 
3487 #ifdef __ANDROID__
disambiguate_qualcomm_chipset(const struct cpuinfo_arm_chipset proc_cpuinfo_hardware_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_product_board_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_board_platform_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_chipname_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_hardware_chipname_chipset[restrict static1])3488 	static inline struct cpuinfo_arm_chipset disambiguate_qualcomm_chipset(
3489 		const struct cpuinfo_arm_chipset proc_cpuinfo_hardware_chipset[restrict static 1],
3490 		const struct cpuinfo_arm_chipset ro_product_board_chipset[restrict static 1],
3491 		const struct cpuinfo_arm_chipset ro_board_platform_chipset[restrict static 1],
3492 		const struct cpuinfo_arm_chipset ro_chipname_chipset[restrict static 1],
3493 		const struct cpuinfo_arm_chipset ro_hardware_chipname_chipset[restrict static 1])
3494 	{
3495 		if (ro_hardware_chipname_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3496 			return *ro_hardware_chipname_chipset;
3497 		}
3498 		if (ro_chipname_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3499 			return *ro_chipname_chipset;
3500 		}
3501 		if (proc_cpuinfo_hardware_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3502 			return *proc_cpuinfo_hardware_chipset;
3503 		}
3504 		if (ro_product_board_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3505 			return *ro_product_board_chipset;
3506 		}
3507 		return *ro_board_platform_chipset;
3508 	}
3509 
disambiguate_mediatek_chipset(const struct cpuinfo_arm_chipset proc_cpuinfo_hardware_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_product_board_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_board_platform_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_mediatek_platform_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_chipname_chipset[restrict static1])3510 	static inline struct cpuinfo_arm_chipset disambiguate_mediatek_chipset(
3511 		const struct cpuinfo_arm_chipset proc_cpuinfo_hardware_chipset[restrict static 1],
3512 		const struct cpuinfo_arm_chipset ro_product_board_chipset[restrict static 1],
3513 		const struct cpuinfo_arm_chipset ro_board_platform_chipset[restrict static 1],
3514 		const struct cpuinfo_arm_chipset ro_mediatek_platform_chipset[restrict static 1],
3515 		const struct cpuinfo_arm_chipset ro_chipname_chipset[restrict static 1])
3516 	{
3517 		if (ro_chipname_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3518 			return *ro_chipname_chipset;
3519 		}
3520 		if (proc_cpuinfo_hardware_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3521 			return *proc_cpuinfo_hardware_chipset;
3522 		}
3523 		if (ro_product_board_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3524 			return *ro_product_board_chipset;
3525 		}
3526 		if (ro_board_platform_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3527 			return *ro_board_platform_chipset;
3528 		}
3529 		return *ro_mediatek_platform_chipset;
3530 	}
3531 
disambiguate_hisilicon_chipset(const struct cpuinfo_arm_chipset proc_cpuinfo_hardware_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_product_board_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_board_platform_chipset[restrict static1])3532 	static inline struct cpuinfo_arm_chipset disambiguate_hisilicon_chipset(
3533 		const struct cpuinfo_arm_chipset proc_cpuinfo_hardware_chipset[restrict static 1],
3534 		const struct cpuinfo_arm_chipset ro_product_board_chipset[restrict static 1],
3535 		const struct cpuinfo_arm_chipset ro_board_platform_chipset[restrict static 1])
3536 	{
3537 		if (proc_cpuinfo_hardware_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3538 			return *proc_cpuinfo_hardware_chipset;
3539 		}
3540 		if (ro_product_board_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3541 			return *ro_product_board_chipset;
3542 		}
3543 		return *ro_board_platform_chipset;
3544 	}
3545 
disambiguate_amlogic_chipset(const struct cpuinfo_arm_chipset proc_cpuinfo_hardware_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_board_platform_chipset[restrict static1])3546 	static inline struct cpuinfo_arm_chipset disambiguate_amlogic_chipset(
3547 		const struct cpuinfo_arm_chipset proc_cpuinfo_hardware_chipset[restrict static 1],
3548 		const struct cpuinfo_arm_chipset ro_board_platform_chipset[restrict static 1])
3549 	{
3550 		if (proc_cpuinfo_hardware_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3551 			return *proc_cpuinfo_hardware_chipset;
3552 		}
3553 		return *ro_board_platform_chipset;
3554 	}
3555 
disambiguate_marvell_chipset(const struct cpuinfo_arm_chipset proc_cpuinfo_hardware_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_product_board_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_chipname_chipset[restrict static1])3556 	static inline struct cpuinfo_arm_chipset disambiguate_marvell_chipset(
3557 		const struct cpuinfo_arm_chipset proc_cpuinfo_hardware_chipset[restrict static 1],
3558 		const struct cpuinfo_arm_chipset ro_product_board_chipset[restrict static 1],
3559 		const struct cpuinfo_arm_chipset ro_chipname_chipset[restrict static 1])
3560 	{
3561 		if (ro_chipname_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3562 			return *ro_chipname_chipset;
3563 		}
3564 		if (ro_product_board_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3565 			return *ro_product_board_chipset;
3566 		}
3567 		return *proc_cpuinfo_hardware_chipset;
3568 	}
3569 
disambiguate_rockchip_chipset(const struct cpuinfo_arm_chipset proc_cpuinfo_hardware_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_product_board_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_board_platform_chipset[restrict static1])3570 	static inline struct cpuinfo_arm_chipset disambiguate_rockchip_chipset(
3571 		const struct cpuinfo_arm_chipset proc_cpuinfo_hardware_chipset[restrict static 1],
3572 		const struct cpuinfo_arm_chipset ro_product_board_chipset[restrict static 1],
3573 		const struct cpuinfo_arm_chipset ro_board_platform_chipset[restrict static 1])
3574 	{
3575 		if (ro_product_board_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3576 			return *ro_product_board_chipset;
3577 		}
3578 		if (proc_cpuinfo_hardware_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3579 			return *proc_cpuinfo_hardware_chipset;
3580 		}
3581 		return *ro_board_platform_chipset;
3582 	}
3583 
disambiguate_spreadtrum_chipset(const struct cpuinfo_arm_chipset proc_cpuinfo_hardware_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_product_board_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_board_platform_chipset[restrict static1],const struct cpuinfo_arm_chipset ro_chipname_chipset[restrict static1])3584 	static inline struct cpuinfo_arm_chipset disambiguate_spreadtrum_chipset(
3585 		const struct cpuinfo_arm_chipset proc_cpuinfo_hardware_chipset[restrict static 1],
3586 		const struct cpuinfo_arm_chipset ro_product_board_chipset[restrict static 1],
3587 		const struct cpuinfo_arm_chipset ro_board_platform_chipset[restrict static 1],
3588 		const struct cpuinfo_arm_chipset ro_chipname_chipset[restrict static 1])
3589 	{
3590 		if (ro_chipname_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3591 			return *ro_chipname_chipset;
3592 		}
3593 		if (ro_product_board_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3594 			return *ro_product_board_chipset;
3595 		}
3596 		if (proc_cpuinfo_hardware_chipset->series != cpuinfo_arm_chipset_series_unknown) {
3597 			return *proc_cpuinfo_hardware_chipset;
3598 		}
3599 		return *ro_board_platform_chipset;
3600 	}
3601 
3602 	/*
3603 	 * Decodes chipset name from Android system properties:
3604 	 * - /proc/cpuinfo Hardware string
3605 	 * - ro.product.board
3606 	 * - ro.board.platform
3607 	 * - ro.mediatek.platform
3608 	 * - ro.chipname
3609 	 * For some chipsets, the function relies frequency and on number of cores for chipset detection.
3610 	 *
3611 	 * @param[in] properties - structure with the Android system properties described above.
3612 	 * @param cores - number of cores in the chipset.
3613 	 * @param max_cpu_freq_max - maximum of /sys/devices/system/cpu/cpu<number>/cpofreq/cpu_freq_max values.
3614 	 *
3615 	 * @returns Decoded chipset name. If chipset could not be decoded, the resulting structure would use `unknown` vendor
3616 	 *          and series identifiers.
3617 	 */
cpuinfo_arm_android_decode_chipset(const struct cpuinfo_android_properties properties[restrict static1],uint32_t cores,uint32_t max_cpu_freq_max)3618 	struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset(
3619 		const struct cpuinfo_android_properties properties[restrict static 1],
3620 		uint32_t cores,
3621 		uint32_t max_cpu_freq_max)
3622 	{
3623 		struct cpuinfo_arm_chipset chipset = {
3624 			.vendor = cpuinfo_arm_chipset_vendor_unknown,
3625 			.series = cpuinfo_arm_chipset_series_unknown,
3626 		};
3627 
3628 		const bool tegra_platform = is_tegra(
3629 			properties->ro_board_platform,
3630 			properties->ro_board_platform + strnlen(properties->ro_board_platform, CPUINFO_BUILD_PROP_VALUE_MAX));
3631 
3632 		struct cpuinfo_arm_chipset chipsets[cpuinfo_android_chipset_property_max] = {
3633 			[cpuinfo_android_chipset_property_proc_cpuinfo_hardware] =
3634 				cpuinfo_arm_linux_decode_chipset_from_proc_cpuinfo_hardware(
3635 					properties->proc_cpuinfo_hardware, cores, max_cpu_freq_max, tegra_platform),
3636 			[cpuinfo_android_chipset_property_ro_product_board] =
3637 				cpuinfo_arm_android_decode_chipset_from_ro_product_board(
3638 					properties->ro_product_board, cores, max_cpu_freq_max),
3639 			[cpuinfo_android_chipset_property_ro_board_platform] =
3640 				cpuinfo_arm_android_decode_chipset_from_ro_board_platform(
3641 					properties->ro_board_platform, cores, max_cpu_freq_max),
3642 			[cpuinfo_android_chipset_property_ro_mediatek_platform] =
3643 				cpuinfo_arm_android_decode_chipset_from_ro_mediatek_platform(properties->ro_mediatek_platform),
3644 			[cpuinfo_android_chipset_property_ro_arch] =
3645 				cpuinfo_arm_android_decode_chipset_from_ro_arch(properties->ro_arch),
3646 			[cpuinfo_android_chipset_property_ro_chipname] =
3647 				cpuinfo_arm_android_decode_chipset_from_ro_chipname(properties->ro_chipname),
3648 			[cpuinfo_android_chipset_property_ro_hardware_chipname] =
3649 				cpuinfo_arm_android_decode_chipset_from_ro_chipname(properties->ro_hardware_chipname),
3650 		};
3651 		enum cpuinfo_arm_chipset_vendor vendor = cpuinfo_arm_chipset_vendor_unknown;
3652 		for (size_t i = 0; i < cpuinfo_android_chipset_property_max; i++) {
3653 			const enum cpuinfo_arm_chipset_vendor decoded_vendor = chipsets[i].vendor;
3654 			if (decoded_vendor != cpuinfo_arm_chipset_vendor_unknown) {
3655 				if (vendor == cpuinfo_arm_chipset_vendor_unknown) {
3656 					vendor = decoded_vendor;
3657 				} else if (vendor != decoded_vendor) {
3658 					/* Parsing different system properties produces different chipset vendors. This situation is rare. */
3659 					cpuinfo_log_error(
3660 						"chipset detection failed: different chipset vendors reported in different system properties");
3661 					goto finish;
3662 				}
3663 			}
3664 		}
3665 		if (vendor == cpuinfo_arm_chipset_vendor_unknown) {
3666 			cpuinfo_log_warning(
3667 				"chipset detection failed: none of the system properties matched known signatures");
3668 			goto finish;
3669 		}
3670 
3671 		/* Fix common bugs in reported chipsets */
3672 		for (size_t i = 0; i < cpuinfo_android_chipset_property_max; i++) {
3673 			cpuinfo_arm_fixup_chipset(&chipsets[i], cores, max_cpu_freq_max);
3674 		}
3675 
3676 		/*
3677 		 * Propagate suffixes: consider all pairs of chipsets, if both chipsets in the pair are from the same series,
3678 		 * and one's suffix is a prefix of another's chipset suffix, use the longest suffix.
3679 		 */
3680 		for (size_t i = 0; i < cpuinfo_android_chipset_property_max; i++) {
3681 			const size_t chipset_i_suffix_length = strnlen(chipsets[i].suffix, CPUINFO_ARM_CHIPSET_SUFFIX_MAX);
3682 			for (size_t j = 0; j < i; j++) {
3683 				if (chipsets[i].series == chipsets[j].series) {
3684 					const size_t chipset_j_suffix_length = strnlen(chipsets[j].suffix, CPUINFO_ARM_CHIPSET_SUFFIX_MAX);
3685 					if (chipset_i_suffix_length != chipset_j_suffix_length) {
3686 						const size_t common_prefix_length = (chipset_i_suffix_length < chipset_j_suffix_length) ?
3687 							chipset_i_suffix_length : chipset_j_suffix_length;
3688 						if (common_prefix_length == 0 ||
3689 							memcmp(chipsets[i].suffix, chipsets[j].suffix, common_prefix_length) == 0)
3690 						{
3691 							if (chipset_i_suffix_length > chipset_j_suffix_length) {
3692 								memcpy(chipsets[j].suffix, chipsets[i].suffix, chipset_i_suffix_length);
3693 							} else {
3694 								memcpy(chipsets[i].suffix, chipsets[j].suffix, chipset_j_suffix_length);
3695 							}
3696 						}
3697 					}
3698 				}
3699 			}
3700 		}
3701 
3702 		for (size_t i = 0; i < cpuinfo_android_chipset_property_max; i++) {
3703 			if (chipsets[i].series != cpuinfo_arm_chipset_series_unknown) {
3704 				if (chipset.series == cpuinfo_arm_chipset_series_unknown) {
3705 					chipset = chipsets[i];
3706 				} else if (chipsets[i].series != chipset.series || chipsets[i].model != chipset.model ||
3707 					strncmp(chipsets[i].suffix, chipset.suffix, CPUINFO_ARM_CHIPSET_SUFFIX_MAX) != 0)
3708 				{
3709 					cpuinfo_log_info(
3710 						"different chipsets reported in different system properties; "
3711 						"vendor-specific disambiguation heuristic would be used");
3712 					switch (vendor) {
3713 						case cpuinfo_arm_chipset_vendor_qualcomm:
3714 							return disambiguate_qualcomm_chipset(
3715 								&chipsets[cpuinfo_android_chipset_property_proc_cpuinfo_hardware],
3716 								&chipsets[cpuinfo_android_chipset_property_ro_product_board],
3717 								&chipsets[cpuinfo_android_chipset_property_ro_board_platform],
3718 								&chipsets[cpuinfo_android_chipset_property_ro_chipname],
3719 								&chipsets[cpuinfo_android_chipset_property_ro_hardware_chipname]);
3720 						case cpuinfo_arm_chipset_vendor_mediatek:
3721 							return disambiguate_mediatek_chipset(
3722 								&chipsets[cpuinfo_android_chipset_property_proc_cpuinfo_hardware],
3723 								&chipsets[cpuinfo_android_chipset_property_ro_product_board],
3724 								&chipsets[cpuinfo_android_chipset_property_ro_board_platform],
3725 								&chipsets[cpuinfo_android_chipset_property_ro_mediatek_platform],
3726 								&chipsets[cpuinfo_android_chipset_property_ro_chipname]);
3727 						case cpuinfo_arm_chipset_vendor_hisilicon:
3728 							return disambiguate_hisilicon_chipset(
3729 								&chipsets[cpuinfo_android_chipset_property_proc_cpuinfo_hardware],
3730 								&chipsets[cpuinfo_android_chipset_property_ro_product_board],
3731 								&chipsets[cpuinfo_android_chipset_property_ro_board_platform]);
3732 						case cpuinfo_arm_chipset_vendor_amlogic:
3733 							return disambiguate_amlogic_chipset(
3734 								&chipsets[cpuinfo_android_chipset_property_proc_cpuinfo_hardware],
3735 								&chipsets[cpuinfo_android_chipset_property_ro_board_platform]);
3736 						case cpuinfo_arm_chipset_vendor_marvell:
3737 							return disambiguate_marvell_chipset(
3738 								&chipsets[cpuinfo_android_chipset_property_proc_cpuinfo_hardware],
3739 								&chipsets[cpuinfo_android_chipset_property_ro_product_board],
3740 								&chipsets[cpuinfo_android_chipset_property_ro_chipname]);
3741 						case cpuinfo_arm_chipset_vendor_rockchip:
3742 							return disambiguate_rockchip_chipset(
3743 								&chipsets[cpuinfo_android_chipset_property_proc_cpuinfo_hardware],
3744 								&chipsets[cpuinfo_android_chipset_property_ro_product_board],
3745 								&chipsets[cpuinfo_android_chipset_property_ro_board_platform]);
3746 						case cpuinfo_arm_chipset_vendor_spreadtrum:
3747 							return disambiguate_spreadtrum_chipset(
3748 								&chipsets[cpuinfo_android_chipset_property_proc_cpuinfo_hardware],
3749 								&chipsets[cpuinfo_android_chipset_property_ro_product_board],
3750 								&chipsets[cpuinfo_android_chipset_property_ro_board_platform],
3751 								&chipsets[cpuinfo_android_chipset_property_ro_chipname]);
3752 						default:
3753 							cpuinfo_log_error(
3754 								"chipset detection failed: "
3755 								"could not disambiguate different chipsets reported in different system properties");
3756 							/* chipset variable contains valid, but inconsistent chipset information, overwrite it */
3757 							chipset = (struct cpuinfo_arm_chipset) {
3758 								.vendor = cpuinfo_arm_chipset_vendor_unknown,
3759 								.series = cpuinfo_arm_chipset_series_unknown,
3760 							};
3761 							goto finish;
3762 					}
3763 				}
3764 			}
3765 		}
3766 
3767 	finish:
3768 		return chipset;
3769 	}
3770 #else /* !defined(__ANDROID__) */
3771 	/*
3772 	 * Fix commonly misreported Broadcom BCM models on Raspberry Pi boards.
3773 	 *
3774 	 * @param[in,out] chipset - chipset name to fix.
3775 	 * @param[in] revision - /proc/cpuinfo Revision string.
3776 	 */
cpuinfo_arm_fixup_raspberry_pi_chipset(struct cpuinfo_arm_chipset chipset[restrict static1],const char revision[restrict static CPUINFO_HARDWARE_VALUE_MAX])3777 	void cpuinfo_arm_fixup_raspberry_pi_chipset(
3778 		struct cpuinfo_arm_chipset chipset[restrict static 1],
3779 		const char revision[restrict static CPUINFO_HARDWARE_VALUE_MAX])
3780 	{
3781 		const size_t revision_length = strnlen(revision, CPUINFO_REVISION_VALUE_MAX);
3782 
3783 		/* Parse revision codes according to https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md */
3784 		#if CPUINFO_ARCH_ARM
3785 			if (revision_length == 4) {
3786 				/*
3787 				 * Old-style revision codes.
3788 				 * All Raspberry Pi models with old-style revision code use Broadcom BCM2835.
3789 				 */
3790 
3791 				/* BCM2835 often misreported as BCM2708 */
3792 				if (chipset->model == 2708) {
3793 					chipset->model = 2835;
3794 				}
3795 				return;
3796 			}
3797 		#endif
3798 		if ((size_t) (revision_length - 5) <= (size_t) (8 - 5) /* 5 <= length(revision) <= 8 */) {
3799 			/* New-style revision codes */
3800 
3801 			uint32_t model = 0;
3802 			switch (revision[revision_length - 4]) {
3803 				case '0':
3804 					/* BCM2835 */
3805 					model = 2835;
3806 					break;
3807 				case '1':
3808 					/* BCM2836 */
3809 					model = 2836;
3810 					break;
3811 				case '2':
3812 					/* BCM2837 */
3813 					model = 2837;
3814 					break;
3815 				case '3':
3816 					/* BCM2711 */
3817 					model = 2711;
3818 					break;
3819 			}
3820 
3821 			if (model != 0) {
3822 				chipset->model = model;
3823 				chipset->suffix[0] = 0;
3824 			}
3825 		}
3826 	}
3827 
3828 	/*
3829 	 * Decodes chipset name from /proc/cpuinfo Hardware string.
3830 	 * For some chipsets, the function relies frequency and on number of cores for chipset detection.
3831 	 *
3832 	 * @param[in] hardware - /proc/cpuinfo Hardware string.
3833 	 * @param cores - number of cores in the chipset.
3834 	 * @param max_cpu_freq_max - maximum of /sys/devices/system/cpu/cpu<number>/cpofreq/cpu_freq_max values.
3835 	 *
3836 	 * @returns Decoded chipset name. If chipset could not be decoded, the resulting structure would use `unknown` vendor
3837 	 *          and series identifiers.
3838 	 */
cpuinfo_arm_linux_decode_chipset(const char hardware[restrict static CPUINFO_HARDWARE_VALUE_MAX],const char revision[restrict static CPUINFO_REVISION_VALUE_MAX],uint32_t cores,uint32_t max_cpu_freq_max)3839 	struct cpuinfo_arm_chipset cpuinfo_arm_linux_decode_chipset(
3840 		const char hardware[restrict static CPUINFO_HARDWARE_VALUE_MAX],
3841 		const char revision[restrict static CPUINFO_REVISION_VALUE_MAX],
3842 		uint32_t cores,
3843 		uint32_t max_cpu_freq_max)
3844 	{
3845 		struct cpuinfo_arm_chipset chipset =
3846 			cpuinfo_arm_linux_decode_chipset_from_proc_cpuinfo_hardware(
3847 				hardware, cores, max_cpu_freq_max, false);
3848 		if (chipset.vendor == cpuinfo_arm_chipset_vendor_unknown) {
3849 			cpuinfo_log_warning(
3850 				"chipset detection failed: /proc/cpuinfo Hardware string did not match known signatures");
3851 		} else if (chipset.vendor == cpuinfo_arm_chipset_vendor_broadcom) {
3852 			/* Raspberry Pi kernel reports bogus chipset models; detect chipset from RPi revision */
3853 			cpuinfo_arm_fixup_raspberry_pi_chipset(&chipset, revision);
3854 		} else {
3855 			cpuinfo_arm_fixup_chipset(&chipset, cores, max_cpu_freq_max);
3856 		}
3857 		return chipset;
3858 	}
3859 
3860 #endif
3861