cpuid: new module
[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
27 #include "cpuid.h"
28
29 enum {
30         CPU_PROC_BRAND_STRING_INTERNAL0                 = 0x80000003,
31         CPU_PROC_BRAND_STRING_INTERNAL1                 = 0x80000004
32 };
33
34 #ifndef _MSC_VER
35 static void ___cpuid(cpuid_t info, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
36 {
37         __asm__(
38                 "xchg %%ebx, %%edi\n\t"         /* 32bit PIC: Don't clobber ebx.  */
39                 "cpuid\n\t"
40                 "xchg %%ebx, %%edi\n\t"
41                 : "=a"(*eax), "=D"(*ebx), "=c"(*ecx), "=d"(*edx)
42                 : "0" (info)
43         );
44 }
45 #else
46 #include <intrin.h>
47
48 static void ___cpuid(cpuid_t info, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
49 {
50         uint32_t registers[4];
51         __cpuid(registers, info);
52
53         *eax = registers[0];
54         *ebx = registers[1];
55         *ecx = registers[2];
56         *edx = registers[3];
57 }
58 #endif
59
60 int highest_ext_func_supported(void)
61 {
62         static int highest;
63
64         if (!highest) {
65                 asm volatile(
66                         "cpuid\n\t"
67                         : "=a" (highest)
68                         : "a" (CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED)
69                 );
70         }
71
72         return highest;
73 }
74
75 int cpuid_test_feature(cpuid_t feature)
76 {
77         if (feature > CPU_VIRT_PHYS_ADDR_SIZES || feature < CPU_EXTENDED_PROC_INFO_FEATURE_BITS)
78                 return 0;
79
80         return (feature <= highest_ext_func_supported());
81 }
82
83 int cpuid_has_feature(cpufeature_t feature)
84 {
85         uint32_t eax, ebx, ecx, edx;
86
87         ___cpuid(CPU_PROCINFO_AND_FEATUREBITS, &eax, &ebx, &ecx, &edx);
88         switch (feature) {
89                 case CF_MMX:
90                 case CF_SSE:
91                 case CF_SSE2:
92                         return (edx & ((int)feature)) != 0;
93                 case CF_SSE3:
94                 case CF_SSSE3:
95                 case CF_SSE41:
96                 case CF_SSE42:
97                 case CF_AVX:
98                 case CF_FMA:
99                         return (ecx & ((int)feature)) != 0;
100         }
101
102         return 0;
103 }
104
105 int cpuid_has_ext_feature(cpuextfeature_t extfeature)
106 {
107         uint32_t eax, ebx, ecx, edx;
108         if (!cpuid_test_feature(CPU_EXTENDED_PROC_INFO_FEATURE_BITS))
109                 return 0;
110
111         ___cpuid(CPU_EXTENDED_PROC_INFO_FEATURE_BITS, &eax, &ebx, &ecx, &edx);
112         switch (extfeature) {
113                 case CEF_x64:
114                         return (edx & ((int)extfeature)) != 0;
115                 case CEF_SSE4a:
116                 case CEF_FMA4:
117                 case CEF_XOP:
118                         return (ecx & ((int)extfeature)) != 0;
119         }
120
121         return 0;
122 }
123
124 void cpuid(cpuid_t info, void *buf)
125 {
126         /* Sanity checks, make sure we're not trying to do something
127          * invalid or we are trying to get information that isn't supported
128          * by the CPU.  */
129         if (info > CPU_VIRT_PHYS_ADDR_SIZES || (info > CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED
130                 && !cpuid_test_feature(info)))
131                 return;
132
133         uint32_t *ubuf = buf;
134         if (info == CPU_PROC_BRAND_STRING) {
135                 ___cpuid(CPU_PROC_BRAND_STRING,           &ubuf[0], &ubuf[1], &ubuf[2],  &ubuf[3]);
136                 ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL0, &ubuf[4], &ubuf[5], &ubuf[6],  &ubuf[7]);
137                 ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL1, &ubuf[8], &ubuf[9], &ubuf[10], &ubuf[11]);
138                 return;
139         } else if (info == CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED) {
140                 *ubuf = highest_ext_func_supported();
141                 return;
142         }
143
144         uint32_t eax, ebx, ecx, edx;
145         ___cpuid(info, &eax, &ebx, &ecx, &edx);
146
147         switch (info) {
148                 case CPU_VENDORID:
149                         ubuf[0] = ebx;
150                         ubuf[1] = edx;
151                         ubuf[2] = ecx;
152                         break;
153                 case CPU_PROCINFO_AND_FEATUREBITS:
154                         ubuf[0] = eax;  /* The so called "signature" of the CPU.  */
155                         ubuf[1] = edx;  /* Feature flags #1.  */
156                         ubuf[2] = ecx;  /* Feature flags #2.  */
157                         ubuf[3] = ebx;  /* Additional feature information.  */
158                         break;
159                 case CPU_CACHE_AND_TLBD_INFO:
160                         ubuf[0] = eax;
161                         ubuf[1] = ebx;
162                         ubuf[2] = ecx;
163                         ubuf[3] = edx;
164                         break;
165                 case CPU_EXTENDED_PROC_INFO_FEATURE_BITS:
166                         ubuf[0] = edx;
167                         ubuf[1] = ecx;
168                         break;
169                 case CPU_L1_CACHE_AND_TLB_IDS:
170                         break;
171                 case CPU_EXTENDED_L2_CACHE_FEATURES:
172                         ubuf[0] = (ecx & 0xFF);                 /* Cache size  */
173                         ubuf[1] = (ecx >> 12) & 0xF;            /* Line size  */
174                         ubuf[2] = (ecx >> 16) & 0xFFFF;         /* Associativity  */
175                         break;
176                 case CPU_ADV_POWER_MGT_INFO:
177                         break;
178                 case CPU_VIRT_PHYS_ADDR_SIZES:
179                         *ubuf = eax;
180                         break;
181                 default:
182                         *ubuf = 0xbaadf00d;
183                         break;
184         }
185 }
186