b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame^] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | #include <sys/types.h> |
| 3 | #include <errno.h> |
| 4 | #include <unistd.h> |
| 5 | #include <stdio.h> |
| 6 | #include <stdlib.h> |
| 7 | #include <string.h> |
| 8 | #include <regex.h> |
| 9 | |
| 10 | #include "../../util/debug.h" |
| 11 | #include "../../util/header.h" |
| 12 | |
| 13 | static inline void |
| 14 | cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c, |
| 15 | unsigned int *d) |
| 16 | { |
| 17 | __asm__ __volatile__ (".byte 0x53\n\tcpuid\n\t" |
| 18 | "movl %%ebx, %%esi\n\t.byte 0x5b" |
| 19 | : "=a" (*a), |
| 20 | "=S" (*b), |
| 21 | "=c" (*c), |
| 22 | "=d" (*d) |
| 23 | : "a" (op)); |
| 24 | } |
| 25 | |
| 26 | static int |
| 27 | __get_cpuid(char *buffer, size_t sz, const char *fmt) |
| 28 | { |
| 29 | unsigned int a, b, c, d, lvl; |
| 30 | int family = -1, model = -1, step = -1; |
| 31 | int nb; |
| 32 | char vendor[16]; |
| 33 | |
| 34 | cpuid(0, &lvl, &b, &c, &d); |
| 35 | strncpy(&vendor[0], (char *)(&b), 4); |
| 36 | strncpy(&vendor[4], (char *)(&d), 4); |
| 37 | strncpy(&vendor[8], (char *)(&c), 4); |
| 38 | vendor[12] = '\0'; |
| 39 | |
| 40 | if (lvl >= 1) { |
| 41 | cpuid(1, &a, &b, &c, &d); |
| 42 | |
| 43 | family = (a >> 8) & 0xf; /* bits 11 - 8 */ |
| 44 | model = (a >> 4) & 0xf; /* Bits 7 - 4 */ |
| 45 | step = a & 0xf; |
| 46 | |
| 47 | /* extended family */ |
| 48 | if (family == 0xf) |
| 49 | family += (a >> 20) & 0xff; |
| 50 | |
| 51 | /* extended model */ |
| 52 | if (family >= 0x6) |
| 53 | model += ((a >> 16) & 0xf) << 4; |
| 54 | } |
| 55 | nb = scnprintf(buffer, sz, fmt, vendor, family, model, step); |
| 56 | |
| 57 | /* look for end marker to ensure the entire data fit */ |
| 58 | if (strchr(buffer, '$')) { |
| 59 | buffer[nb-1] = '\0'; |
| 60 | return 0; |
| 61 | } |
| 62 | return ENOBUFS; |
| 63 | } |
| 64 | |
| 65 | int |
| 66 | get_cpuid(char *buffer, size_t sz) |
| 67 | { |
| 68 | return __get_cpuid(buffer, sz, "%s,%u,%u,%u$"); |
| 69 | } |
| 70 | |
| 71 | char * |
| 72 | get_cpuid_str(struct perf_pmu *pmu __maybe_unused) |
| 73 | { |
| 74 | char *buf = malloc(128); |
| 75 | |
| 76 | if (buf && __get_cpuid(buf, 128, "%s-%u-%X-%X$") < 0) { |
| 77 | free(buf); |
| 78 | return NULL; |
| 79 | } |
| 80 | return buf; |
| 81 | } |
| 82 | |
| 83 | /* Full CPUID format for x86 is vendor-family-model-stepping */ |
| 84 | static bool is_full_cpuid(const char *id) |
| 85 | { |
| 86 | const char *tmp = id; |
| 87 | int count = 0; |
| 88 | |
| 89 | while ((tmp = strchr(tmp, '-')) != NULL) { |
| 90 | count++; |
| 91 | tmp++; |
| 92 | } |
| 93 | |
| 94 | if (count == 3) |
| 95 | return true; |
| 96 | |
| 97 | return false; |
| 98 | } |
| 99 | |
| 100 | int strcmp_cpuid_str(const char *mapcpuid, const char *id) |
| 101 | { |
| 102 | regex_t re; |
| 103 | regmatch_t pmatch[1]; |
| 104 | int match; |
| 105 | bool full_mapcpuid = is_full_cpuid(mapcpuid); |
| 106 | bool full_cpuid = is_full_cpuid(id); |
| 107 | |
| 108 | /* |
| 109 | * Full CPUID format is required to identify a platform. |
| 110 | * Error out if the cpuid string is incomplete. |
| 111 | */ |
| 112 | if (full_mapcpuid && !full_cpuid) { |
| 113 | pr_info("Invalid CPUID %s. Full CPUID is required, " |
| 114 | "vendor-family-model-stepping\n", id); |
| 115 | return 1; |
| 116 | } |
| 117 | |
| 118 | if (regcomp(&re, mapcpuid, REG_EXTENDED) != 0) { |
| 119 | /* Warn unable to generate match particular string. */ |
| 120 | pr_info("Invalid regular expression %s\n", mapcpuid); |
| 121 | return 1; |
| 122 | } |
| 123 | |
| 124 | match = !regexec(&re, id, 1, pmatch, 0); |
| 125 | regfree(&re); |
| 126 | if (match) { |
| 127 | size_t match_len = (pmatch[0].rm_eo - pmatch[0].rm_so); |
| 128 | size_t cpuid_len; |
| 129 | |
| 130 | /* If the full CPUID format isn't required, |
| 131 | * ignoring the stepping. |
| 132 | */ |
| 133 | if (!full_mapcpuid && full_cpuid) |
| 134 | cpuid_len = strrchr(id, '-') - id; |
| 135 | else |
| 136 | cpuid_len = strlen(id); |
| 137 | |
| 138 | /* Verify the entire string matched. */ |
| 139 | if (match_len == cpuid_len) |
| 140 | return 0; |
| 141 | } |
| 142 | |
| 143 | return 1; |
| 144 | } |