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