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