resetting manifest requested domain to floor
[platform/upstream/x86info.git] / cpuid.c
1 /*
2  *  (C) 2000, 2001 Dave Jones.
3  *  Fixes by Arjan van de Ven (arjanv@redhat.com) and
4  *  Philipp Rumpf (prumpf@mandrakesoft.com)
5  *
6  *  Licensed under the terms of the GNU GPL License version 2.
7  *
8  *  Routines for retrieving cpuid registers.
9  */
10
11 #define _LARGEFILE64_SOURCE
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <fcntl.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20 #include <errno.h>
21
22 #define __USE_GNU
23 #include <sched.h>
24
25 #if defined(__FreeBSD__)
26 # include <sys/ioctl.h>
27 # include <cpu.h>
28 #endif
29
30 #include "x86info.h"
31
32 /*
33  * sched_* calls weren't stable until 2.3.4
34  * AFAIK, there's no macro to check for the .4, so we just
35  * check for the next minor version up. (2.4)
36  */
37 #ifdef __GLIBC__
38 #if __GLIBC__ < 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ < 4
39 #error Need at least glibc 2.4
40 #endif
41 #endif
42
43 /* returns zero on success */
44 static int native_cpuid(unsigned int cpunr, unsigned long long idx,
45         unsigned int *eax, unsigned int *ebx,
46         unsigned int *ecx, unsigned int *edx)
47 {
48         cpu_set_t set, tmp_set;
49         unsigned int a = 0, b = 0, c = 0, d = 0;
50         int ret;
51
52         if (eax != NULL)
53                 a = *eax;
54         if (ebx != NULL)
55                 b = *ebx;
56         if (ecx != NULL)
57                 c = *ecx;
58         if (edx != NULL)
59                 d = *edx;
60
61         ret = sched_getaffinity(getpid(), sizeof(set), &set);
62         if (ret)
63                 return ret;
64
65         /* man CPU_SET(3): To duplicate a CPU set, use memcpy(3) */
66         memcpy(&tmp_set, &set, sizeof(cpu_set_t));
67         CPU_ZERO(&set);
68         CPU_SET(cpunr, &set);
69         ret = sched_setaffinity(getpid(), sizeof(set), &set);
70         if (ret)
71                 return ret;
72
73         asm("cpuid"
74                 : "=a" (a),
75                   "=b" (b),
76                   "+c" (c),
77                   "=d" (d)
78                 : "0" ((unsigned int)idx));
79
80         if (eax!=NULL)
81                 *eax = a;
82         if (ebx!=NULL)
83                 *ebx = b;
84         if (ecx!=NULL)
85                 *ecx = c;
86         if (edx!=NULL)
87                 *edx = d;
88
89         /* Restore initial sched affinity */
90         ret = sched_setaffinity(getpid(), sizeof(tmp_set), &tmp_set);
91         if (ret)
92                 return ret;
93         return 0;
94 }
95
96 static const char *NATIVE_CPUID_FAILED_MSG = "WARNING: Native cpuid failed\n";
97
98 #if defined(__FreeBSD__)
99 void cpuid(unsigned int CPU_number, unsigned long long idx,
100         unsigned int *eax,
101         unsigned int *ebx,
102         unsigned int *ecx,
103         unsigned int *edx)
104 {
105         static int nodriver=0;
106         char cpuname[20];
107         unsigned char buffer[16];
108         int fh;
109         cpu_cpuid_args_t args;
110
111         if (nodriver == 1) {
112                 if (native_cpuid(CPU_number, idx, eax,ebx,ecx,edx))
113                         printf("%s", NATIVE_CPUID_FAILED_MSG);
114                 return;
115         }
116
117         args.level = idx;
118         /* Ok, use the /dev/CPU interface in preference to the _up code. */
119         (void)snprintf(cpuname,18, "/dev/cpu%d", CPU_number);
120         fh = open(cpuname, O_RDONLY);
121         if (fh != -1) {
122                 if (ioctl(fh, CPU_CPUID, &args) != 0) {
123                         perror(cpuname);
124                         exit(EXIT_FAILURE);
125                 }
126                 if (eax!=0)     *eax = args.data[0];
127                 if (ebx!=0)     *ebx = args.data[1];
128                 if (ecx!=0)     *ecx = args.data[2];
129                 if (edx!=0)     *edx = args.data[3];
130                 if (close(fh) == -1) {
131                         perror("close");
132                         exit(EXIT_FAILURE);
133                 }
134         } else {
135                 /* Something went wrong, just do UP and hope for the best. */
136                 nodriver = 1;
137                 if (nrCPUs != 1)
138                         perror(cpuname);
139                 if (native_cpuid(CPU_number, idx, eax,ebx,ecx,edx))
140                         printf("%s", NATIVE_CPUID_FAILED_MSG);
141
142                 return;
143         }
144 }
145
146 #else /* !__FreeBSD__ */
147
148 /* Kernel CPUID driver's minimum supported read size
149  * (see linux/arch/i386/kernel/cpuid.c)
150  */
151 #define CPUID_CHUNK_SIZE (16)
152
153 void cpuid(unsigned int CPU_number, unsigned long long idx,
154         unsigned int *eax,
155         unsigned int *ebx,
156         unsigned int *ecx,
157         unsigned int *edx)
158 {
159         static int nodriver=0;
160         char cpuname[20];
161         unsigned char buffer[CPUID_CHUNK_SIZE];
162         unsigned int *ptr = (unsigned int *)buffer;
163         int fh;
164
165         if (eax != NULL) {
166                 *eax = (unsigned int) idx;
167                 if (*eax == 4)
168                         *ecx = idx >> 32;
169         }
170
171         if (nodriver == 1) {
172                 if (native_cpuid(CPU_number, idx, eax,ebx,ecx,edx))
173                         printf("%s", NATIVE_CPUID_FAILED_MSG);
174                 return;
175         }
176
177         memset(cpuname, 0, sizeof(cpuname));
178 #ifdef __sun__
179         /* Solaris doesn't (yet) have per-CPU interface */
180         (void)snprintf(cpuname, sizeof(cpuname), "/dev/cpu/self/cpuid");
181 #else
182         /* Ok, use the /dev/cpu interface in preference to the _up code. */
183         (void)snprintf(cpuname, sizeof(cpuname), "/dev/cpu/%d/cpuid", CPU_number);
184 #endif
185         fh = open(cpuname, O_RDONLY);
186         if (fh != -1) {
187 #ifndef S_SPLINT_S
188                 lseek64(fh, (off64_t)idx, SEEK_CUR);
189 #endif
190                 if (read(fh, &buffer[0], CPUID_CHUNK_SIZE) == -1) {
191                         perror(cpuname);
192                         exit(EXIT_FAILURE);
193                 }
194                 if (eax!=0)     *eax = *ptr;
195                 if (ebx!=0)     *ebx = *(++ptr);
196                 if (ecx!=0)     *ecx = *(++ptr);
197                 if (edx!=0)     *edx = *(++ptr);
198                 if (close(fh) == -1) {
199                         perror("close");
200                         exit(EXIT_FAILURE);
201                 }
202         } else {
203                 /* Something went wrong, just do UP and hope for the best. */
204                 nodriver = 1;
205                 if (native_cpuid(CPU_number, idx, eax,ebx,ecx,edx))
206                         printf("%s", NATIVE_CPUID_FAILED_MSG);
207                 return;
208         }
209 }
210
211 #endif /* __FreeBSD__ */
212
213 void cpuid4(unsigned int CPU_number, unsigned long long idx,
214         unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx)
215 {
216         cpuid(CPU_number, 4 | (idx << 32), eax, ebx, ecx, edx);
217 }
218
219 /* Some CPUID calls want 'count' to be placed in ecx */
220 void cpuid_count(unsigned int CPU_number, unsigned int op, int count,
221         unsigned int *eax, unsigned int *ebx,
222         unsigned int *ecx, unsigned int *edx)
223 {
224         *ecx = count;
225         cpuid(CPU_number, op, eax, ebx, ecx, edx);
226 }
227
228 unsigned int cpuid_ebx(unsigned int CPU_number, unsigned int op)
229 {
230         unsigned int eax, ebx, ecx, edx;
231
232         cpuid(CPU_number, op, &eax, &ebx, &ecx, &edx);
233
234         return ebx;
235 }
236
237
238 void dump_raw_cpuid(int cpunum, unsigned int begin, unsigned int end)
239 {
240         unsigned int i;
241         unsigned int eax, ebx, ecx, edx;
242
243         /* Dump all the CPUID results in raw hex */
244         for (i = begin; i <= end; i++) {
245                 cpuid(cpunum, i, &eax, &ebx, &ecx, &edx);
246                 printf("eax in: 0x%08x, eax = %08x ebx = %08x ecx = %08x edx = %08x\n", i, eax, ebx, ecx, edx);
247         }
248         printf("\n");
249 }