xref: /aosp_15_r20/external/cpuinfo/src/x86/linux/cpuinfo.c (revision 2b54f0db79fd8303838913b20ff3780cddaa909f)
1*2b54f0dbSXin Li #include <stdbool.h>
2*2b54f0dbSXin Li #include <stdint.h>
3*2b54f0dbSXin Li #include <stdlib.h>
4*2b54f0dbSXin Li #include <stddef.h>
5*2b54f0dbSXin Li #include <string.h>
6*2b54f0dbSXin Li 
7*2b54f0dbSXin Li #include <linux/api.h>
8*2b54f0dbSXin Li #include <x86/linux/api.h>
9*2b54f0dbSXin Li #include <cpuinfo/log.h>
10*2b54f0dbSXin Li 
11*2b54f0dbSXin Li /*
12*2b54f0dbSXin Li  * Size, in chars, of the on-stack buffer used for parsing lines of /proc/cpuinfo.
13*2b54f0dbSXin Li  * This is also the limit on the length of a single line.
14*2b54f0dbSXin Li  */
15*2b54f0dbSXin Li #define BUFFER_SIZE 2048
16*2b54f0dbSXin Li 
17*2b54f0dbSXin Li 
parse_processor_number(const char * processor_start,const char * processor_end)18*2b54f0dbSXin Li static uint32_t parse_processor_number(
19*2b54f0dbSXin Li 	const char* processor_start,
20*2b54f0dbSXin Li 	const char* processor_end)
21*2b54f0dbSXin Li {
22*2b54f0dbSXin Li 	const size_t processor_length = (size_t) (processor_end - processor_start);
23*2b54f0dbSXin Li 
24*2b54f0dbSXin Li 	if (processor_length == 0) {
25*2b54f0dbSXin Li 		cpuinfo_log_warning("Processor number in /proc/cpuinfo is ignored: string is empty");
26*2b54f0dbSXin Li 		return 0;
27*2b54f0dbSXin Li 	}
28*2b54f0dbSXin Li 
29*2b54f0dbSXin Li 	uint32_t processor_number = 0;
30*2b54f0dbSXin Li 	for (const char* digit_ptr = processor_start; digit_ptr != processor_end; digit_ptr++) {
31*2b54f0dbSXin Li 		const uint32_t digit = (uint32_t) (*digit_ptr - '0');
32*2b54f0dbSXin Li 		if (digit > 10) {
33*2b54f0dbSXin Li 			cpuinfo_log_warning("non-decimal suffix %.*s in /proc/cpuinfo processor number is ignored",
34*2b54f0dbSXin Li 				(int) (processor_end - digit_ptr), digit_ptr);
35*2b54f0dbSXin Li 			break;
36*2b54f0dbSXin Li 		}
37*2b54f0dbSXin Li 
38*2b54f0dbSXin Li 		processor_number = processor_number * 10 + digit;
39*2b54f0dbSXin Li 	}
40*2b54f0dbSXin Li 
41*2b54f0dbSXin Li 	return processor_number;
42*2b54f0dbSXin Li }
43*2b54f0dbSXin Li 
44*2b54f0dbSXin Li /*
45*2b54f0dbSXin Li  * Decode APIC ID reported by Linux kernel for x86/x86-64 architecture.
46*2b54f0dbSXin Li  * Example of APIC ID reported in /proc/cpuinfo:
47*2b54f0dbSXin Li  *
48*2b54f0dbSXin Li  *		apicid		: 2
49*2b54f0dbSXin Li  */
parse_apic_id(const char * apic_start,const char * apic_end,struct cpuinfo_x86_linux_processor processor[restrict static1])50*2b54f0dbSXin Li static void parse_apic_id(
51*2b54f0dbSXin Li 	const char* apic_start,
52*2b54f0dbSXin Li 	const char* apic_end,
53*2b54f0dbSXin Li 	struct cpuinfo_x86_linux_processor processor[restrict static 1])
54*2b54f0dbSXin Li {
55*2b54f0dbSXin Li 	uint32_t apic_id = 0;
56*2b54f0dbSXin Li 	for (const char* digit_ptr = apic_start; digit_ptr != apic_end; digit_ptr++) {
57*2b54f0dbSXin Li 		const uint32_t digit = *digit_ptr - '0';
58*2b54f0dbSXin Li 		if (digit >= 10) {
59*2b54f0dbSXin Li 			cpuinfo_log_warning("APIC ID %.*s in /proc/cpuinfo is ignored due to unexpected non-digit character '%c' at offset %zu",
60*2b54f0dbSXin Li 				(int) (apic_end - apic_start), apic_start,
61*2b54f0dbSXin Li 				*digit_ptr, (size_t) (digit_ptr - apic_start));
62*2b54f0dbSXin Li 			return;
63*2b54f0dbSXin Li 		}
64*2b54f0dbSXin Li 
65*2b54f0dbSXin Li 		apic_id = apic_id * 10 + digit;
66*2b54f0dbSXin Li 	}
67*2b54f0dbSXin Li 
68*2b54f0dbSXin Li 	processor->apic_id = apic_id;
69*2b54f0dbSXin Li 	processor->flags |= CPUINFO_LINUX_FLAG_APIC_ID;
70*2b54f0dbSXin Li }
71*2b54f0dbSXin Li 
72*2b54f0dbSXin Li struct proc_cpuinfo_parser_state {
73*2b54f0dbSXin Li 	uint32_t processor_index;
74*2b54f0dbSXin Li 	uint32_t max_processors_count;
75*2b54f0dbSXin Li 	struct cpuinfo_x86_linux_processor* processors;
76*2b54f0dbSXin Li 	struct cpuinfo_x86_linux_processor dummy_processor;
77*2b54f0dbSXin Li };
78*2b54f0dbSXin Li 
79*2b54f0dbSXin Li /*
80*2b54f0dbSXin Li  *	Decode a single line of /proc/cpuinfo information.
81*2b54f0dbSXin Li  *	Lines have format <words-with-spaces>[ ]*:[ ]<space-separated words>
82*2b54f0dbSXin Li  */
parse_line(const char * line_start,const char * line_end,struct proc_cpuinfo_parser_state state[restrict static1],uint64_t line_number)83*2b54f0dbSXin Li static bool parse_line(
84*2b54f0dbSXin Li 	const char* line_start,
85*2b54f0dbSXin Li 	const char* line_end,
86*2b54f0dbSXin Li 	struct proc_cpuinfo_parser_state state[restrict static 1],
87*2b54f0dbSXin Li 	uint64_t line_number)
88*2b54f0dbSXin Li {
89*2b54f0dbSXin Li 	/* Empty line. Skip. */
90*2b54f0dbSXin Li 	if (line_start == line_end) {
91*2b54f0dbSXin Li 		return true;
92*2b54f0dbSXin Li 	}
93*2b54f0dbSXin Li 
94*2b54f0dbSXin Li 	/* Search for ':' on the line. */
95*2b54f0dbSXin Li 	const char* separator = line_start;
96*2b54f0dbSXin Li 	for (; separator != line_end; separator++) {
97*2b54f0dbSXin Li 		if (*separator == ':') {
98*2b54f0dbSXin Li 			break;
99*2b54f0dbSXin Li 		}
100*2b54f0dbSXin Li 	}
101*2b54f0dbSXin Li 	/* Skip line if no ':' separator was found. */
102*2b54f0dbSXin Li 	if (separator == line_end) {
103*2b54f0dbSXin Li 		cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: key/value separator ':' not found",
104*2b54f0dbSXin Li 			(int) (line_end - line_start), line_start);
105*2b54f0dbSXin Li 		return true;
106*2b54f0dbSXin Li 	}
107*2b54f0dbSXin Li 
108*2b54f0dbSXin Li 	/* Skip trailing spaces in key part. */
109*2b54f0dbSXin Li 	const char* key_end = separator;
110*2b54f0dbSXin Li 	for (; key_end != line_start; key_end--) {
111*2b54f0dbSXin Li 		if (key_end[-1] != ' ' && key_end[-1] != '\t') {
112*2b54f0dbSXin Li 			break;
113*2b54f0dbSXin Li 		}
114*2b54f0dbSXin Li 	}
115*2b54f0dbSXin Li 	/* Skip line if key contains nothing but spaces. */
116*2b54f0dbSXin Li 	if (key_end == line_start) {
117*2b54f0dbSXin Li 		cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: key contains only spaces",
118*2b54f0dbSXin Li 			(int) (line_end - line_start), line_start);
119*2b54f0dbSXin Li 		return true;
120*2b54f0dbSXin Li 	}
121*2b54f0dbSXin Li 
122*2b54f0dbSXin Li 	/* Skip leading spaces in value part. */
123*2b54f0dbSXin Li 	const char* value_start = separator + 1;
124*2b54f0dbSXin Li 	for (; value_start != line_end; value_start++) {
125*2b54f0dbSXin Li 		if (*value_start != ' ') {
126*2b54f0dbSXin Li 			break;
127*2b54f0dbSXin Li 		}
128*2b54f0dbSXin Li 	}
129*2b54f0dbSXin Li 	/* Value part contains nothing but spaces. Skip line. */
130*2b54f0dbSXin Li 	if (value_start == line_end) {
131*2b54f0dbSXin Li 		cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: value contains only spaces",
132*2b54f0dbSXin Li 			(int) (line_end - line_start), line_start);
133*2b54f0dbSXin Li 		return true;
134*2b54f0dbSXin Li 	}
135*2b54f0dbSXin Li 
136*2b54f0dbSXin Li 	/* Skip trailing spaces in value part (if any) */
137*2b54f0dbSXin Li 	const char* value_end = line_end;
138*2b54f0dbSXin Li 	for (; value_end != value_start; value_end--) {
139*2b54f0dbSXin Li 		if (value_end[-1] != ' ') {
140*2b54f0dbSXin Li 			break;
141*2b54f0dbSXin Li 		}
142*2b54f0dbSXin Li 	}
143*2b54f0dbSXin Li 
144*2b54f0dbSXin Li 	const uint32_t processor_index      = state->processor_index;
145*2b54f0dbSXin Li 	const uint32_t max_processors_count = state->max_processors_count;
146*2b54f0dbSXin Li 	struct cpuinfo_x86_linux_processor* processors = state->processors;
147*2b54f0dbSXin Li 	struct cpuinfo_x86_linux_processor* processor  = &state->dummy_processor;
148*2b54f0dbSXin Li 	if (processor_index < max_processors_count) {
149*2b54f0dbSXin Li 		processor = &processors[processor_index];
150*2b54f0dbSXin Li 	}
151*2b54f0dbSXin Li 
152*2b54f0dbSXin Li 	const size_t key_length = key_end - line_start;
153*2b54f0dbSXin Li 	switch (key_length) {
154*2b54f0dbSXin Li 		case 6:
155*2b54f0dbSXin Li 			if (memcmp(line_start, "apicid", key_length) == 0) {
156*2b54f0dbSXin Li 				parse_apic_id(value_start, value_end, processor);
157*2b54f0dbSXin Li 			} else {
158*2b54f0dbSXin Li 				goto unknown;
159*2b54f0dbSXin Li 			}
160*2b54f0dbSXin Li 			break;
161*2b54f0dbSXin Li 		case 9:
162*2b54f0dbSXin Li 			if (memcmp(line_start, "processor", key_length) == 0) {
163*2b54f0dbSXin Li 				const uint32_t new_processor_index = parse_processor_number(value_start, value_end);
164*2b54f0dbSXin Li 				if (new_processor_index < processor_index) {
165*2b54f0dbSXin Li 					/* Strange: decreasing processor number */
166*2b54f0dbSXin Li 					cpuinfo_log_warning(
167*2b54f0dbSXin Li 						"unexpectedly low processor number %"PRIu32" following processor %"PRIu32" in /proc/cpuinfo",
168*2b54f0dbSXin Li 						new_processor_index, processor_index);
169*2b54f0dbSXin Li 				} else if (new_processor_index > processor_index + 1) {
170*2b54f0dbSXin Li 					/* Strange, but common: skipped processor $(processor_index + 1) */
171*2b54f0dbSXin Li 					cpuinfo_log_info(
172*2b54f0dbSXin Li 						"unexpectedly high processor number %"PRIu32" following processor %"PRIu32" in /proc/cpuinfo",
173*2b54f0dbSXin Li 						new_processor_index, processor_index);
174*2b54f0dbSXin Li 				}
175*2b54f0dbSXin Li 				if (new_processor_index >= max_processors_count) {
176*2b54f0dbSXin Li 					/* Log and ignore processor */
177*2b54f0dbSXin Li 					cpuinfo_log_warning("processor %"PRIu32" in /proc/cpuinfo is ignored: index exceeds system limit %"PRIu32,
178*2b54f0dbSXin Li 						new_processor_index, max_processors_count - 1);
179*2b54f0dbSXin Li 				} else {
180*2b54f0dbSXin Li 					processors[new_processor_index].flags |= CPUINFO_LINUX_FLAG_PROC_CPUINFO;
181*2b54f0dbSXin Li 				}
182*2b54f0dbSXin Li 				state->processor_index = new_processor_index;
183*2b54f0dbSXin Li 				return true;
184*2b54f0dbSXin Li 			} else {
185*2b54f0dbSXin Li 				goto unknown;
186*2b54f0dbSXin Li 			}
187*2b54f0dbSXin Li 			break;
188*2b54f0dbSXin Li 		default:
189*2b54f0dbSXin Li 		unknown:
190*2b54f0dbSXin Li 			cpuinfo_log_debug("unknown /proc/cpuinfo key: %.*s", (int) key_length, line_start);
191*2b54f0dbSXin Li 
192*2b54f0dbSXin Li 	}
193*2b54f0dbSXin Li 	return true;
194*2b54f0dbSXin Li }
195*2b54f0dbSXin Li 
cpuinfo_x86_linux_parse_proc_cpuinfo(uint32_t max_processors_count,struct cpuinfo_x86_linux_processor processors[restrict static max_processors_count])196*2b54f0dbSXin Li bool cpuinfo_x86_linux_parse_proc_cpuinfo(
197*2b54f0dbSXin Li 	uint32_t max_processors_count,
198*2b54f0dbSXin Li 	struct cpuinfo_x86_linux_processor processors[restrict static max_processors_count])
199*2b54f0dbSXin Li {
200*2b54f0dbSXin Li 	struct proc_cpuinfo_parser_state state = {
201*2b54f0dbSXin Li 		.processor_index = 0,
202*2b54f0dbSXin Li 		.max_processors_count = max_processors_count,
203*2b54f0dbSXin Li 		.processors = processors,
204*2b54f0dbSXin Li 	};
205*2b54f0dbSXin Li 	return cpuinfo_linux_parse_multiline_file("/proc/cpuinfo", BUFFER_SIZE,
206*2b54f0dbSXin Li 		(cpuinfo_line_callback) parse_line, &state);
207*2b54f0dbSXin Li }
208