cpuid: add 2 new functions + some more tests
[ccan] / ccan / cpuid / cpuid.c
1 /*
2  * Copyright (c) 2013 Ahmed Samy  <f.fallen45@gmail.com>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  *
22  * This file has been written with some help from wikipedia:
23  *      http://en.wikipedia.org/wiki/CPUID
24  */
25 #include <stdint.h>
26 #include <string.h>
27
28 #include "cpuid.h"
29
30 enum {
31         CPU_PROC_BRAND_STRING_INTERNAL0                 = 0x80000003,
32         CPU_PROC_BRAND_STRING_INTERNAL1                 = 0x80000004
33 };
34
35 #ifndef _MSC_VER
36 static void ___cpuid(cpuid_t info, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
37 {
38         __asm__(
39                 "xchg %%ebx, %%edi\n\t"         /* 32bit PIC: Don't clobber ebx.  */
40                 "cpuid\n\t"
41                 "xchg %%ebx, %%edi\n\t"
42                 : "=a"(*eax), "=D"(*ebx), "=c"(*ecx), "=d"(*edx)
43                 : "0" (info)
44         );
45 }
46 #else
47 #include <intrin.h>
48
49 static void ___cpuid(cpuid_t info, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
50 {
51         uint32_t registers[4];
52         __cpuid(registers, info);
53
54         *eax = registers[0];
55         *ebx = registers[1];
56         *ecx = registers[2];
57         *edx = registers[3];
58 }
59 #endif
60
61 int highest_ext_func_supported(void)
62 {
63         static int highest;
64
65         if (!highest) {
66                 asm volatile(
67                         "cpuid\n\t"
68                         : "=a" (highest)
69                         : "a" (CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED)
70                 );
71         }
72
73         return highest;
74 }
75
76 int cpuid_test_feature(cpuid_t feature)
77 {
78         if (feature > CPU_VIRT_PHYS_ADDR_SIZES || feature < CPU_EXTENDED_PROC_INFO_FEATURE_BITS)
79                 return 0;
80
81         return (feature <= highest_ext_func_supported());
82 }
83
84 int cpuid_has_feature(cpufeature_t feature)
85 {
86         uint32_t eax, ebx, ecx, edx;
87
88         ___cpuid(CPU_PROCINFO_AND_FEATUREBITS, &eax, &ebx, &ecx, &edx);
89         switch (feature) {
90                 case CF_MMX:
91                 case CF_SSE:
92                 case CF_SSE2:
93                         return (edx & ((int)feature)) != 0;
94                 case CF_SSE3:
95                 case CF_SSSE3:
96                 case CF_SSE41:
97                 case CF_SSE42:
98                 case CF_AVX:
99                 case CF_FMA:
100                         return (ecx & ((int)feature)) != 0;
101         }
102
103         return 0;
104 }
105
106 int cpuid_has_ext_feature(cpuextfeature_t extfeature)
107 {
108         uint32_t eax, ebx, ecx, edx;
109         if (!cpuid_test_feature(CPU_EXTENDED_PROC_INFO_FEATURE_BITS))
110                 return 0;
111
112         ___cpuid(CPU_EXTENDED_PROC_INFO_FEATURE_BITS, &eax, &ebx, &ecx, &edx);
113         switch (extfeature) {
114                 case CEF_x64:
115                         return (edx & ((int)extfeature)) != 0;
116                 case CEF_SSE4a:
117                 case CEF_FMA4:
118                 case CEF_XOP:
119                         return (ecx & ((int)extfeature)) != 0;
120         }
121
122         return 0;
123 }
124
125 static const char *cpuids[] = {
126         "Nooooooooone",
127         "AMDisbetter!",
128         "AuthenticAMD",
129         "CentaurHauls",
130         "CyrixInstead",
131         "GenuineIntel",
132         "TransmetaCPU",
133         "GeniuneTMx86",
134         "Geode by NSC",
135         "NexGenDriven",
136         "RiseRiseRise",
137         "SiS SiS SiS ",
138         "UMC UMC UMC ",
139         "VIA VIA VIA ",
140         "Vortex86 SoC",
141         "KVMKVMKVMKVM"
142 };
143
144 cputype_t get_cpu_type(void)
145 {
146         static cputype_t cputype;
147         if (cputype == CT_NONE) {
148                 union {
149                         char buf[12];
150                         uint32_t bufu32[3];
151                 } u;
152                 uint32_t i;
153
154                 ___cpuid(CPU_VENDORID, &i, &u.bufu32[0], &u.bufu32[2], &u.bufu32[1]);
155                 u.buf[12] = '\0';
156
157                 for (i = 0; i < sizeof(cpuids) / sizeof(cpuids[0]); ++i) {
158                         if (strncmp(cpuids[i], u.buf, 12) == 0) {
159                                 cputype = (cputype_t)i;
160                                 break;
161                         }
162                 }
163         }
164
165         return cputype;
166 }
167
168 const char *get_cpu_type_string(const cputype_t cputype)
169 {
170         return cpuids[(int)cputype];
171 }
172
173 void cpuid(cpuid_t info, void *buf)
174 {
175         /* Sanity checks, make sure we're not trying to do something
176          * invalid or we are trying to get information that isn't supported
177          * by the CPU.  */
178         if (info > CPU_VIRT_PHYS_ADDR_SIZES || (info > CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED
179                 && !cpuid_test_feature(info)))
180                 return;
181
182         uint32_t *ubuf = buf;
183         if (info == CPU_PROC_BRAND_STRING) {
184                 ___cpuid(CPU_PROC_BRAND_STRING,           &ubuf[0], &ubuf[1], &ubuf[2],  &ubuf[3]);
185                 ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL0, &ubuf[4], &ubuf[5], &ubuf[6],  &ubuf[7]);
186                 ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL1, &ubuf[8], &ubuf[9], &ubuf[10], &ubuf[11]);
187                 return;
188         } else if (info == CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED) {
189                 *ubuf = highest_ext_func_supported();
190                 return;
191         }
192
193         uint32_t eax, ebx, ecx, edx;
194         ___cpuid(info, &eax, &ebx, &ecx, &edx);
195
196         switch (info) {
197                 case CPU_VENDORID:
198                         ubuf[0] = ebx;
199                         ubuf[1] = edx;
200                         ubuf[2] = ecx;
201                         break;
202                 case CPU_PROCINFO_AND_FEATUREBITS:
203                         ubuf[0] = eax;  /* The so called "signature" of the CPU.  */
204                         ubuf[1] = edx;  /* Feature flags #1.  */
205                         ubuf[2] = ecx;  /* Feature flags #2.  */
206                         ubuf[3] = ebx;  /* Additional feature information.  */
207                         break;
208                 case CPU_CACHE_AND_TLBD_INFO:
209                         ubuf[0] = eax;
210                         ubuf[1] = ebx;
211                         ubuf[2] = ecx;
212                         ubuf[3] = edx;
213                         break;
214                 case CPU_EXTENDED_PROC_INFO_FEATURE_BITS:
215                         ubuf[0] = edx;
216                         ubuf[1] = ecx;
217                         break;
218                 case CPU_L1_CACHE_AND_TLB_IDS:
219                         break;
220                 case CPU_EXTENDED_L2_CACHE_FEATURES:
221                         *ubuf = ecx;
222                         break;
223                 case CPU_ADV_POWER_MGT_INFO:
224                         *ubuf = edx;
225                         break;
226                 case CPU_VIRT_PHYS_ADDR_SIZES:
227                         *ubuf = eax;
228                         break;
229                 default:
230                         *ubuf = 0xbaadf00d;
231                         break;
232         }
233 }
234