1 #include <stdbool.h>
2 #include <stdint.h>
3 #include <stddef.h>
4 #include <stdio.h>
5 #include <string.h>
6
7 #include <cpuinfo.h>
8 #include <cpuinfo/common.h>
9 #include <x86/api.h>
10
11
12 /* The state of the parser to be preserved between parsing different tokens. */
13 struct parser_state {
14 /*
15 * Pointer to the start of the previous token if it is "model".
16 * NULL if previous token is not "model".
17 */
18 char* context_model;
19 /*
20 * Pointer to the start of the previous token if it is a single-uppercase-letter token.
21 * NULL if previous token is anything different.
22 */
23 char* context_upper_letter;
24 /*
25 * Pointer to the start of the previous token if it is "Dual".
26 * NULL if previous token is not "Dual".
27 */
28 char* context_dual;
29 /*
30 * Pointer to the start of the previous token if it is "Core", "Dual-Core", "QuadCore", etc.
31 * NULL if previous token is anything different.
32 */
33 char* context_core;
34 /*
35 * Pointer to the start of the previous token if it is "Eng" or "Engineering", etc.
36 * NULL if previous token is anything different.
37 */
38 char* context_engineering;
39 /*
40 * Pointer to the '@' symbol in the brand string (separates frequency specification).
41 * NULL if there is no '@' symbol.
42 */
43 char* frequency_separator;
44 /* Indicates whether the brand string (after transformations) contains frequency. */
45 bool frequency_token;
46 /* Indicates whether the processor is of Xeon family (contains "Xeon" substring). */
47 bool xeon;
48 /* Indicates whether the processor model number was already parsed. */
49 bool parsed_model_number;
50 /* Indicates whether the processor is an engineering sample (contains "Engineering Sample" or "Eng Sample" substrings). */
51 bool engineering_sample;
52 };
53
54 /** @brief Resets information about the previous token. Keeps all other state information. */
reset_context(struct parser_state * state)55 static void reset_context(struct parser_state* state) {
56 state->context_model = NULL;
57 state->context_upper_letter = NULL;
58 state->context_dual = NULL;
59 state->context_core = NULL;
60 }
61
62 /**
63 * @brief Overwrites the supplied string with space characters if it exactly matches the given string.
64 * @param string The string to be compared against other string, and erased in case of matching.
65 * @param length The length of the two string to be compared against each other.
66 * @param target The string to compare against.
67 * @retval true If the two strings match and the first supplied string was erased (overwritten with space characters).
68 * @retval false If the two strings are different and the first supplied string remained unchanged.
69 */
erase_matching(char * string,size_t length,const char * target)70 static inline bool erase_matching(char* string, size_t length, const char* target) {
71 const bool match = memcmp(string, target, length) == 0;
72 if (match) {
73 memset(string, ' ', length);
74 }
75 return match;
76 }
77
78 /**
79 * @brief Checks if the supplied ASCII character is an uppercase latin letter.
80 * @param character The character to analyse.
81 * @retval true If the supplied character is an uppercase latin letter ('A' to 'Z').
82 * @retval false If the supplied character is anything different.
83 */
is_upper_letter(char character)84 static inline bool is_upper_letter(char character) {
85 return (uint32_t) (character - 'A') <= (uint32_t)('Z' - 'A');
86 }
87
88 /**
89 * @brief Checks if the supplied ASCII character is a digit.
90 * @param character The character to analyse.
91 * @retval true If the supplied character is a digit ('0' to '9').
92 * @retval false If the supplied character is anything different.
93 */
is_digit(char character)94 static inline bool is_digit(char character) {
95 return (uint32_t) (character - '0') < UINT32_C(10);
96 }
97
is_zero_number(const char * token_start,const char * token_end)98 static inline bool is_zero_number(const char* token_start, const char* token_end) {
99 for (const char* char_ptr = token_start; char_ptr != token_end; char_ptr++) {
100 if (*char_ptr != '0') {
101 return false;
102 }
103 }
104 return true;
105 }
106
is_space(const char * token_start,const char * token_end)107 static inline bool is_space(const char* token_start, const char* token_end) {
108 for (const char* char_ptr = token_start; char_ptr != token_end; char_ptr++) {
109 if (*char_ptr != ' ') {
110 return false;
111 }
112 }
113 return true;
114 }
115
is_number(const char * token_start,const char * token_end)116 static inline bool is_number(const char* token_start, const char* token_end) {
117 for (const char* char_ptr = token_start; char_ptr != token_end; char_ptr++) {
118 if (!is_digit(*char_ptr)) {
119 return false;
120 }
121 }
122 return true;
123 }
124
is_model_number(const char * token_start,const char * token_end)125 static inline bool is_model_number(const char* token_start, const char* token_end) {
126 for (const char* char_ptr = token_start + 1; char_ptr < token_end; char_ptr++) {
127 if (is_digit(char_ptr[-1]) && is_digit(char_ptr[0])) {
128 return true;
129 }
130 }
131 return false;
132 }
133
is_frequency(const char * token_start,const char * token_end)134 static inline bool is_frequency(const char* token_start, const char* token_end) {
135 const size_t token_length = (size_t) (token_end - token_start);
136 if (token_length > 3 && token_end[-2] == 'H' && token_end[-1] == 'z') {
137 switch (token_end[-3]) {
138 case 'K':
139 case 'M':
140 case 'G':
141 return true;
142 }
143 }
144 return false;
145 }
146
147 /**
148 * @warning Input and output tokens can overlap
149 */
move_token(const char * token_start,const char * token_end,char * output_ptr)150 static inline char* move_token(const char* token_start, const char* token_end, char* output_ptr) {
151 const size_t token_length = (size_t) (token_end - token_start);
152 memmove(output_ptr, token_start, token_length);
153 return output_ptr + token_length;
154 }
155
transform_token(char * token_start,char * token_end,struct parser_state * state)156 static bool transform_token(char* token_start, char* token_end, struct parser_state* state) {
157 const struct parser_state previousState = *state;
158 reset_context(state);
159
160 size_t token_length = (size_t) (token_end - token_start);
161
162 if (state->frequency_separator != NULL) {
163 if (token_start > state->frequency_separator) {
164 if (state->parsed_model_number) {
165 memset(token_start, ' ', token_length);
166 }
167 }
168 }
169
170
171 /* Early AMD and Cyrix processors have "tm" suffix for trademark, e.g.
172 * "AMD-K6tm w/ multimedia extensions"
173 * "Cyrix MediaGXtm MMXtm Enhanced"
174 */
175 if (token_length > 2) {
176 const char context_char = token_end[-3];
177 if (is_digit(context_char) || is_upper_letter(context_char)) {
178 if (erase_matching(token_end - 2, 2, "tm")) {
179 token_end -= 2;
180 token_length -= 2;
181 }
182 }
183 }
184 if (token_length > 4) {
185 /* Some early AMD CPUs have "AMD-" at the beginning, e.g.
186 * "AMD-K5(tm) Processor"
187 * "AMD-K6tm w/ multimedia extensions"
188 * "AMD-K6(tm) 3D+ Processor"
189 * "AMD-K6(tm)-III Processor"
190 */
191 if (erase_matching(token_start, 4, "AMD-")) {
192 token_start += 4;
193 token_length -= 4;
194 }
195 }
196 switch (token_length) {
197 case 1:
198 /*
199 * On some Intel processors there is a space between the first letter of
200 * the name and the number after it, e.g.
201 * "Intel(R) Core(TM) i7 CPU X 990 @ 3.47GHz"
202 * "Intel(R) Core(TM) CPU Q 820 @ 1.73GHz"
203 * We want to merge these parts together, in reverse order, i.e. "X 990" -> "990X", "820" -> "820Q"
204 */
205 if (is_upper_letter(token_start[0])) {
206 state->context_upper_letter = token_start;
207 return true;
208 }
209 break;
210 case 2:
211 /* Erase everything after "w/" in "AMD-K6tm w/ multimedia extensions" */
212 if (erase_matching(token_start, token_length, "w/")) {
213 return false;
214 }
215 /*
216 * Intel Xeon processors since Ivy Bridge use versions, e.g.
217 * "Intel Xeon E3-1230 v2"
218 * Some processor branch strings report them as "V<N>", others report as "v<N>".
219 * Normalize the former (upper-case) to the latter (lower-case) version
220 */
221 if (token_start[0] == 'V' && is_digit(token_start[1])) {
222 token_start[0] = 'v';
223 return true;
224 }
225 break;
226 case 3:
227 /*
228 * Erase "CPU" in brand string on Intel processors, e.g.
229 * "Intel(R) Core(TM) i5 CPU 650 @ 3.20GHz"
230 * "Intel(R) Xeon(R) CPU X3210 @ 2.13GHz"
231 * "Intel(R) Atom(TM) CPU Z2760 @ 1.80GHz"
232 */
233 if (erase_matching(token_start, token_length, "CPU")) {
234 return true;
235 }
236 /*
237 * Erase everywhing after "SOC" on AMD System-on-Chips, e.g.
238 * "AMD GX-212JC SOC with Radeon(TM) R2E Graphics \0"
239 */
240 if (erase_matching(token_start, token_length, "SOC")) {
241 return false;
242 }
243 /*
244 * Erase "AMD" in brand string on AMD processors, e.g.
245 * "AMD Athlon(tm) Processor"
246 * "AMD Engineering Sample"
247 * "Quad-Core AMD Opteron(tm) Processor 2344 HE"
248 */
249 if (erase_matching(token_start, token_length, "AMD")) {
250 return true;
251 }
252 /*
253 * Erase "VIA" in brand string on VIA processors, e.g.
254 * "VIA C3 Ezra"
255 * "VIA C7-M Processor 1200MHz"
256 * "VIA Nano L3050@1800MHz"
257 */
258 if (erase_matching(token_start, token_length, "VIA")) {
259 return true;
260 }
261 /* Erase "IDT" in brand string on early Centaur processors, e.g. "IDT WinChip 2-3D" */
262 if (erase_matching(token_start, token_length, "IDT")) {
263 return true;
264 }
265 /*
266 * Erase everything starting with "MMX" in
267 * "Cyrix MediaGXtm MMXtm Enhanced" ("tm" suffix is removed by this point)
268 */
269 if (erase_matching(token_start, token_length, "MMX")) {
270 return false;
271 }
272 /*
273 * Erase everything starting with "APU" on AMD processors, e.g.
274 * "AMD A10-4600M APU with Radeon(tm) HD Graphics"
275 * "AMD A10-7850K APU with Radeon(TM) R7 Graphics"
276 * "AMD A6-6310 APU with AMD Radeon R4 Graphics"
277 */
278 if (erase_matching(token_start, token_length, "APU")) {
279 return false;
280 }
281 /*
282 * Remember to discard string if it contains "Eng Sample",
283 * e.g. "Eng Sample, ZD302046W4K43_36/30/20_2/8_A"
284 */
285 if (memcmp(token_start, "Eng", token_length) == 0) {
286 state->context_engineering = token_start;
287 }
288 break;
289 case 4:
290 /* Remember to erase "Dual Core" in "AMD Athlon(tm) 64 X2 Dual Core Processor 3800+" */
291 if (memcmp(token_start, "Dual", token_length) == 0) {
292 state->context_dual = token_start;
293 }
294 /* Remember if the processor is on Xeon family */
295 if (memcmp(token_start, "Xeon", token_length) == 0) {
296 state->xeon = true;
297 }
298 /* Erase "Dual Core" in "AMD Athlon(tm) 64 X2 Dual Core Processor 3800+" */
299 if (previousState.context_dual != NULL) {
300 if (memcmp(token_start, "Core", token_length) == 0) {
301 memset(previousState.context_dual, ' ', (size_t) (token_end - previousState.context_dual));
302 state->context_core = token_end;
303 return true;
304 }
305 }
306 break;
307 case 5:
308 /*
309 * Erase "Intel" in brand string on Intel processors, e.g.
310 * "Intel(R) Xeon(R) CPU X3210 @ 2.13GHz"
311 * "Intel(R) Atom(TM) CPU D2700 @ 2.13GHz"
312 * "Genuine Intel(R) processor 800MHz"
313 */
314 if (erase_matching(token_start, token_length, "Intel")) {
315 return true;
316 }
317 /*
318 * Erase "Cyrix" in brand string on Cyrix processors, e.g.
319 * "Cyrix MediaGXtm MMXtm Enhanced"
320 */
321 if (erase_matching(token_start, token_length, "Cyrix")) {
322 return true;
323 }
324 /*
325 * Erase everything following "Geode" (but not "Geode" token itself) on Geode processors, e.g.
326 * "Geode(TM) Integrated Processor by AMD PCS"
327 * "Geode(TM) Integrated Processor by National Semi"
328 */
329 if (memcmp(token_start, "Geode", token_length) == 0) {
330 return false;
331 }
332 /* Remember to erase "model unknown" in "AMD Processor model unknown" */
333 if (memcmp(token_start, "model", token_length) == 0) {
334 state->context_model = token_start;
335 return true;
336 }
337 break;
338 case 6:
339 /*
340 * Erase everything starting with "Radeon" or "RADEON" on AMD APUs, e.g.
341 * "A8-7670K Radeon R7, 10 Compute Cores 4C+6G"
342 * "FX-8800P Radeon R7, 12 Compute Cores 4C+8G"
343 * "A12-9800 RADEON R7, 12 COMPUTE CORES 4C+8G"
344 * "A9-9410 RADEON R5, 5 COMPUTE CORES 2C+3G"
345 */
346 if (erase_matching(token_start, token_length, "Radeon") || erase_matching(token_start, token_length, "RADEON")) {
347 return false;
348 }
349 /*
350 * Erase "Mobile" when it is not part of the processor name,
351 * e.g. in "AMD Turion(tm) X2 Ultra Dual-Core Mobile ZM-82"
352 */
353 if (previousState.context_core != NULL) {
354 if (erase_matching(token_start, token_length, "Mobile")) {
355 return true;
356 }
357 }
358 /* Erase "family" in "Intel(R) Pentium(R) III CPU family 1266MHz" */
359 if (erase_matching(token_start, token_length, "family")) {
360 return true;
361 }
362 /* Discard the string if it contains "Engineering Sample" */
363 if (previousState.context_engineering != NULL) {
364 if (memcmp(token_start, "Sample", token_length) == 0) {
365 state->engineering_sample = true;
366 return false;
367 }
368 }
369 break;
370 case 7:
371 /*
372 * Erase "Geniune" in brand string on Intel engineering samples, e.g.
373 * "Genuine Intel(R) processor 800MHz"
374 * "Genuine Intel(R) CPU @ 2.13GHz"
375 * "Genuine Intel(R) CPU 0000 @ 1.73GHz"
376 */
377 if (erase_matching(token_start, token_length, "Genuine")) {
378 return true;
379 }
380 /*
381 * Erase "12-core" in brand string on AMD Threadripper, e.g.
382 * "AMD Ryzen Threadripper 1920X 12-Core Processor"
383 */
384 if (erase_matching(token_start, token_length, "12-Core")) {
385 return true;
386 }
387 /*
388 * Erase "16-core" in brand string on AMD Threadripper, e.g.
389 * "AMD Ryzen Threadripper 1950X 16-Core Processor"
390 */
391 if (erase_matching(token_start, token_length, "16-Core")) {
392 return true;
393 }
394 /* Erase "model unknown" in "AMD Processor model unknown" */
395 if (previousState.context_model != NULL) {
396 if (memcmp(token_start, "unknown", token_length) == 0) {
397 memset(previousState.context_model, ' ', token_end - previousState.context_model);
398 return true;
399 }
400 }
401 /*
402 * Discard the string if it contains "Eng Sample:" or "Eng Sample," e.g.
403 * "AMD Eng Sample, ZD302046W4K43_36/30/20_2/8_A"
404 * "AMD Eng Sample: 2D3151A2M88E4_35/31_N"
405 */
406 if (previousState.context_engineering != NULL) {
407 if (memcmp(token_start, "Sample,", token_length) == 0 || memcmp(token_start, "Sample:", token_length) == 0) {
408 state->engineering_sample = true;
409 return false;
410 }
411 }
412 break;
413 case 8:
414 /* Erase "QuadCore" in "VIA QuadCore L4700 @ 1.2+ GHz" */
415 if (erase_matching(token_start, token_length, "QuadCore")) {
416 state->context_core = token_end;
417 return true;
418 }
419 /* Erase "Six-Core" in "AMD FX(tm)-6100 Six-Core Processor" */
420 if (erase_matching(token_start, token_length, "Six-Core")) {
421 state->context_core = token_end;
422 return true;
423 }
424 break;
425 case 9:
426 if (erase_matching(token_start, token_length, "Processor")) {
427 return true;
428 }
429 if (erase_matching(token_start, token_length, "processor")) {
430 return true;
431 }
432 /* Erase "Dual-Core" in "Pentium(R) Dual-Core CPU T4200 @ 2.00GHz" */
433 if (erase_matching(token_start, token_length, "Dual-Core")) {
434 state->context_core = token_end;
435 return true;
436 }
437 /* Erase "Quad-Core" in AMD processors, e.g.
438 * "Quad-Core AMD Opteron(tm) Processor 2347 HE"
439 * "AMD FX(tm)-4170 Quad-Core Processor"
440 */
441 if (erase_matching(token_start, token_length, "Quad-Core")) {
442 state->context_core = token_end;
443 return true;
444 }
445 /* Erase "Transmeta" in brand string on Transmeta processors, e.g.
446 * "Transmeta(tm) Crusoe(tm) Processor TM5800"
447 * "Transmeta Efficeon(tm) Processor TM8000"
448 */
449 if (erase_matching(token_start, token_length, "Transmeta")) {
450 return true;
451 }
452 break;
453 case 10:
454 /*
455 * Erase "Eight-Core" in AMD processors, e.g.
456 * "AMD FX(tm)-8150 Eight-Core Processor"
457 */
458 if (erase_matching(token_start, token_length, "Eight-Core")) {
459 state->context_core = token_end;
460 return true;
461 }
462 break;
463 case 11:
464 /*
465 * Erase "Triple-Core" in AMD processors, e.g.
466 * "AMD Phenom(tm) II N830 Triple-Core Processor"
467 * "AMD Phenom(tm) 8650 Triple-Core Processor"
468 */
469 if (erase_matching(token_start, token_length, "Triple-Core")) {
470 state->context_core = token_end;
471 return true;
472 }
473 /*
474 * Remember to discard string if it contains "Engineering Sample",
475 * e.g. "AMD Engineering Sample"
476 */
477 if (memcmp(token_start, "Engineering", token_length) == 0) {
478 state->context_engineering = token_start;
479 return true;
480 }
481 break;
482 }
483 if (is_zero_number(token_start, token_end)) {
484 memset(token_start, ' ', token_length);
485 return true;
486 }
487 /* On some Intel processors the last letter of the name is put before the number,
488 * and an additional space it added, e.g.
489 * "Intel(R) Core(TM) i7 CPU X 990 @ 3.47GHz"
490 * "Intel(R) Core(TM) CPU Q 820 @ 1.73GHz"
491 * "Intel(R) Core(TM) i5 CPU M 480 @ 2.67GHz"
492 * We fix this issue, i.e. "X 990" -> "990X", "Q 820" -> "820Q"
493 */
494 if (previousState.context_upper_letter != 0) {
495 /* A single letter token followed by 2-to-5 digit letter is merged together */
496 switch (token_length) {
497 case 2:
498 case 3:
499 case 4:
500 case 5:
501 if (is_number(token_start, token_end)) {
502 /* Load the previous single-letter token */
503 const char letter = *previousState.context_upper_letter;
504 /* Erase the previous single-letter token */
505 *previousState.context_upper_letter = ' ';
506 /* Move the current token one position to the left */
507 move_token(token_start, token_end, token_start - 1);
508 token_start -= 1;
509 /*
510 * Add the letter on the end
511 * Note: accessing token_start[-1] is safe because this is not the first token
512 */
513 token_end[-1] = letter;
514 }
515 }
516 }
517 if (state->frequency_separator != NULL) {
518 if (is_model_number(token_start, token_end)) {
519 state->parsed_model_number = true;
520 }
521 }
522 if (is_frequency(token_start, token_end)) {
523 state->frequency_token = true;
524 }
525 return true;
526 }
527
cpuinfo_x86_normalize_brand_string(const char raw_name[48],char normalized_name[48])528 uint32_t cpuinfo_x86_normalize_brand_string(
529 const char raw_name[48],
530 char normalized_name[48])
531 {
532 normalized_name[0] = '\0';
533 char name[48];
534 memcpy(name, raw_name, sizeof(name));
535
536 /*
537 * First find the end of the string
538 * Start search from the end because some brand strings contain zeroes in the middle
539 */
540 char* name_end = &name[48];
541 while (name_end[-1] == '\0') {
542 /*
543 * Adject name_end by 1 position and
544 * check that we didn't reach the start of the brand string.
545 * This is possible if all characters are zero.
546 */
547 if (--name_end == name) {
548 /* All characters are zeros */
549 return 0;
550 }
551 }
552
553 struct parser_state parser_state = { 0 };
554
555 /* Now unify all whitespace characters: replace tabs and '\0' with spaces */
556 {
557 bool inside_parentheses = false;
558 for (char* char_ptr = name; char_ptr != name_end; char_ptr++) {
559 switch (*char_ptr) {
560 case '(':
561 inside_parentheses = true;
562 *char_ptr = ' ';
563 break;
564 case ')':
565 inside_parentheses = false;
566 *char_ptr = ' ';
567 break;
568 case '@':
569 parser_state.frequency_separator = char_ptr;
570 case '\0':
571 case '\t':
572 *char_ptr = ' ';
573 break;
574 default:
575 if (inside_parentheses) {
576 *char_ptr = ' ';
577 }
578 }
579 }
580 }
581
582 /* Iterate through all tokens and erase redundant parts */
583 {
584 bool is_token = false;
585 char* token_start;
586 for (char* char_ptr = name; char_ptr != name_end; char_ptr++) {
587 if (*char_ptr == ' ') {
588 if (is_token) {
589 is_token = false;
590 if (!transform_token(token_start, char_ptr, &parser_state)) {
591 name_end = char_ptr;
592 break;
593 }
594 }
595 } else {
596 if (!is_token) {
597 is_token = true;
598 token_start = char_ptr;
599 }
600 }
601 }
602 if (is_token) {
603 transform_token(token_start, name_end, &parser_state);
604 }
605 }
606
607 /* If this is an engineering sample, return empty string */
608 if (parser_state.engineering_sample) {
609 return 0;
610 }
611
612 /* Check if there is some string before the frequency separator. */
613 if (parser_state.frequency_separator != NULL) {
614 if (is_space(name, parser_state.frequency_separator)) {
615 /* If only frequency is available, return empty string */
616 return 0;
617 }
618 }
619
620 /* Compact tokens: collapse multiple spacing into one */
621 {
622 char* output_ptr = normalized_name;
623 char* token_start;
624 bool is_token = false;
625 bool previous_token_ends_with_dash = true;
626 bool current_token_starts_with_dash = false;
627 uint32_t token_count = 1;
628 for (char* char_ptr = name; char_ptr != name_end; char_ptr++) {
629 const char character = *char_ptr;
630 if (character == ' ') {
631 if (is_token) {
632 is_token = false;
633 if (!current_token_starts_with_dash && !previous_token_ends_with_dash) {
634 token_count += 1;
635 *output_ptr++ = ' ';
636 }
637 output_ptr = move_token(token_start, char_ptr, output_ptr);
638 /* Note: char_ptr[-1] exists because there is a token before this space */
639 previous_token_ends_with_dash = (char_ptr[-1] == '-');
640 }
641 } else {
642 if (!is_token) {
643 is_token = true;
644 token_start = char_ptr;
645 current_token_starts_with_dash = (character == '-');
646 }
647 }
648 }
649 if (is_token) {
650 if (!current_token_starts_with_dash && !previous_token_ends_with_dash) {
651 token_count += 1;
652 *output_ptr++ = ' ';
653 }
654 output_ptr = move_token(token_start, name_end, output_ptr);
655 }
656 if (parser_state.frequency_token && token_count <= 1) {
657 /* The only remaining part is frequency */
658 normalized_name[0] = '\0';
659 return 0;
660 }
661 if (output_ptr < &normalized_name[48]) {
662 *output_ptr = '\0';
663 } else {
664 normalized_name[47] = '\0';
665 }
666 return (uint32_t) (output_ptr - normalized_name);
667 }
668 }
669
670 static const char* vendor_string_map[] = {
671 [cpuinfo_vendor_intel] = "Intel",
672 [cpuinfo_vendor_amd] = "AMD",
673 [cpuinfo_vendor_via] = "VIA",
674 [cpuinfo_vendor_rdc] = "RDC",
675 [cpuinfo_vendor_dmp] = "DM&P",
676 [cpuinfo_vendor_transmeta] = "Transmeta",
677 [cpuinfo_vendor_cyrix] = "Cyrix",
678 [cpuinfo_vendor_rise] = "Rise",
679 [cpuinfo_vendor_nsc] = "NSC",
680 [cpuinfo_vendor_sis] = "SiS",
681 [cpuinfo_vendor_nexgen] = "NexGen",
682 [cpuinfo_vendor_umc] = "UMC",
683 };
684
cpuinfo_x86_format_package_name(enum cpuinfo_vendor vendor,const char normalized_brand_string[48],char package_name[CPUINFO_PACKAGE_NAME_MAX])685 uint32_t cpuinfo_x86_format_package_name(
686 enum cpuinfo_vendor vendor,
687 const char normalized_brand_string[48],
688 char package_name[CPUINFO_PACKAGE_NAME_MAX])
689 {
690 if (normalized_brand_string[0] == '\0') {
691 package_name[0] = '\0';
692 return 0;
693 }
694
695 const char* vendor_string = NULL;
696 if ((uint32_t) vendor < (uint32_t) CPUINFO_COUNT_OF(vendor_string_map)) {
697 vendor_string = vendor_string_map[(uint32_t) vendor];
698 }
699 if (vendor_string == NULL) {
700 strncpy(package_name, normalized_brand_string, CPUINFO_PACKAGE_NAME_MAX);
701 package_name[CPUINFO_PACKAGE_NAME_MAX - 1] = '\0';
702 return 0;
703 } else {
704 snprintf(package_name, CPUINFO_PACKAGE_NAME_MAX,
705 "%s %s", vendor_string, normalized_brand_string);
706 return strlen(vendor_string) + 1;
707 }
708 }
709