xref: /kvmtool/x86/cpuid.c (revision a1fe6bc58d58a8dbe4c51bbccd06a32a4af16bf9)
1*a1fe6bc5SPekka Enberg #include "kvm/kvm.h"
2*a1fe6bc5SPekka Enberg 
3*a1fe6bc5SPekka Enberg #include "kvm/util.h"
4*a1fe6bc5SPekka Enberg 
5*a1fe6bc5SPekka Enberg #include <sys/ioctl.h>
6*a1fe6bc5SPekka Enberg #include <stdlib.h>
7*a1fe6bc5SPekka Enberg #include <assert.h>
8*a1fe6bc5SPekka Enberg 
9*a1fe6bc5SPekka Enberg struct cpuid_regs {
10*a1fe6bc5SPekka Enberg 	uint32_t	eax;
11*a1fe6bc5SPekka Enberg 	uint32_t	ebx;
12*a1fe6bc5SPekka Enberg 	uint32_t	ecx;
13*a1fe6bc5SPekka Enberg 	uint32_t 	edx;
14*a1fe6bc5SPekka Enberg };
15*a1fe6bc5SPekka Enberg 
16*a1fe6bc5SPekka Enberg static inline void
17*a1fe6bc5SPekka Enberg cpuid(struct cpuid_regs *regs)
18*a1fe6bc5SPekka Enberg {
19*a1fe6bc5SPekka Enberg 	asm("cpuid"
20*a1fe6bc5SPekka Enberg 	    : "=a" (regs->eax),
21*a1fe6bc5SPekka Enberg 	      "=b" (regs->ebx),
22*a1fe6bc5SPekka Enberg 	      "=c" (regs->ecx),
23*a1fe6bc5SPekka Enberg 	      "=d" (regs->edx)
24*a1fe6bc5SPekka Enberg 	    : "0" (regs->eax), "2" (regs->ecx));
25*a1fe6bc5SPekka Enberg }
26*a1fe6bc5SPekka Enberg 
27*a1fe6bc5SPekka Enberg static struct kvm_cpuid2 *kvm_cpuid__new(unsigned long nent)
28*a1fe6bc5SPekka Enberg {
29*a1fe6bc5SPekka Enberg 	struct kvm_cpuid2 *self;
30*a1fe6bc5SPekka Enberg 
31*a1fe6bc5SPekka Enberg 	self		= calloc(1, sizeof(*self) + (sizeof(struct kvm_cpuid_entry2) * nent));
32*a1fe6bc5SPekka Enberg 
33*a1fe6bc5SPekka Enberg 	if (!self)
34*a1fe6bc5SPekka Enberg 		die("out of memory");
35*a1fe6bc5SPekka Enberg 
36*a1fe6bc5SPekka Enberg 	return self;
37*a1fe6bc5SPekka Enberg }
38*a1fe6bc5SPekka Enberg 
39*a1fe6bc5SPekka Enberg static void kvm_cpuid__delete(struct kvm_cpuid2 *self)
40*a1fe6bc5SPekka Enberg {
41*a1fe6bc5SPekka Enberg 	free(self);
42*a1fe6bc5SPekka Enberg }
43*a1fe6bc5SPekka Enberg 
44*a1fe6bc5SPekka Enberg #define	MAX_KVM_CPUID_ENTRIES		100
45*a1fe6bc5SPekka Enberg 
46*a1fe6bc5SPekka Enberg enum {
47*a1fe6bc5SPekka Enberg 	CPUID_GET_VENDOR_ID		= 0x00,
48*a1fe6bc5SPekka Enberg 	CPUID_GET_HIGHEST_EXT_FUNCTION	= 0x80000000,
49*a1fe6bc5SPekka Enberg };
50*a1fe6bc5SPekka Enberg 
51*a1fe6bc5SPekka Enberg static uint32_t cpuid_highest_ext_func(void)
52*a1fe6bc5SPekka Enberg {
53*a1fe6bc5SPekka Enberg 	struct cpuid_regs regs;
54*a1fe6bc5SPekka Enberg 
55*a1fe6bc5SPekka Enberg 	regs	= (struct cpuid_regs) {
56*a1fe6bc5SPekka Enberg 		.eax		= CPUID_GET_HIGHEST_EXT_FUNCTION,
57*a1fe6bc5SPekka Enberg 	};
58*a1fe6bc5SPekka Enberg 	cpuid(&regs);
59*a1fe6bc5SPekka Enberg 
60*a1fe6bc5SPekka Enberg 	return regs.eax;
61*a1fe6bc5SPekka Enberg }
62*a1fe6bc5SPekka Enberg 
63*a1fe6bc5SPekka Enberg static uint32_t cpuid_highest_func(void)
64*a1fe6bc5SPekka Enberg {
65*a1fe6bc5SPekka Enberg 	struct cpuid_regs regs;
66*a1fe6bc5SPekka Enberg 
67*a1fe6bc5SPekka Enberg 	regs	= (struct cpuid_regs) {
68*a1fe6bc5SPekka Enberg 		.eax		= CPUID_GET_VENDOR_ID,
69*a1fe6bc5SPekka Enberg 	};
70*a1fe6bc5SPekka Enberg 	cpuid(&regs);
71*a1fe6bc5SPekka Enberg 
72*a1fe6bc5SPekka Enberg 	return regs.eax;
73*a1fe6bc5SPekka Enberg }
74*a1fe6bc5SPekka Enberg 
75*a1fe6bc5SPekka Enberg void kvm__setup_cpuid(struct kvm *self)
76*a1fe6bc5SPekka Enberg {
77*a1fe6bc5SPekka Enberg 	struct kvm_cpuid2 *kvm_cpuid;
78*a1fe6bc5SPekka Enberg 	uint32_t highest_ext;
79*a1fe6bc5SPekka Enberg 	uint32_t function;
80*a1fe6bc5SPekka Enberg 	uint32_t highest;
81*a1fe6bc5SPekka Enberg 	uint32_t ndx = 0;
82*a1fe6bc5SPekka Enberg 
83*a1fe6bc5SPekka Enberg 	kvm_cpuid	= kvm_cpuid__new(MAX_KVM_CPUID_ENTRIES);
84*a1fe6bc5SPekka Enberg 
85*a1fe6bc5SPekka Enberg 	highest		= cpuid_highest_func();
86*a1fe6bc5SPekka Enberg 
87*a1fe6bc5SPekka Enberg 	for (function = 0; function < highest; function++) {
88*a1fe6bc5SPekka Enberg 		/*
89*a1fe6bc5SPekka Enberg 		 * NOTE NOTE NOTE! Functions 0x0b and 0x0d seem to need special
90*a1fe6bc5SPekka Enberg 		 * treatment as per qemu sources but we treat them as regular
91*a1fe6bc5SPekka Enberg 		 * CPUID functions here because they are fairly exotic and the
92*a1fe6bc5SPekka Enberg 		 * Linux kernel is not interested in them during boot up.
93*a1fe6bc5SPekka Enberg 		 */
94*a1fe6bc5SPekka Enberg 		switch (function) {
95*a1fe6bc5SPekka Enberg 		case 0x02: {	/* Processor configuration descriptor */
96*a1fe6bc5SPekka Enberg 			struct cpuid_regs regs;
97*a1fe6bc5SPekka Enberg 			uint32_t times;
98*a1fe6bc5SPekka Enberg 
99*a1fe6bc5SPekka Enberg 			regs	= (struct cpuid_regs) {
100*a1fe6bc5SPekka Enberg 				.eax		= function,
101*a1fe6bc5SPekka Enberg 			};
102*a1fe6bc5SPekka Enberg 			cpuid(&regs);
103*a1fe6bc5SPekka Enberg 
104*a1fe6bc5SPekka Enberg 			kvm_cpuid->entries[ndx++]	= (struct kvm_cpuid_entry2) {
105*a1fe6bc5SPekka Enberg 				.function	= function,
106*a1fe6bc5SPekka Enberg 				.index		= 0,
107*a1fe6bc5SPekka Enberg 				.flags		= KVM_CPUID_FLAG_STATEFUL_FUNC | KVM_CPUID_FLAG_STATE_READ_NEXT,
108*a1fe6bc5SPekka Enberg 				.eax		= regs.eax,
109*a1fe6bc5SPekka Enberg 				.ebx		= regs.ebx,
110*a1fe6bc5SPekka Enberg 				.ecx		= regs.ecx,
111*a1fe6bc5SPekka Enberg 				.edx		= regs.edx,
112*a1fe6bc5SPekka Enberg 			};
113*a1fe6bc5SPekka Enberg 
114*a1fe6bc5SPekka Enberg 			times	= regs.eax & 0xff;	/* AL */
115*a1fe6bc5SPekka Enberg 
116*a1fe6bc5SPekka Enberg 			while (times-- > 0) {
117*a1fe6bc5SPekka Enberg 				regs	= (struct cpuid_regs) {
118*a1fe6bc5SPekka Enberg 					.eax		= function,
119*a1fe6bc5SPekka Enberg 				};
120*a1fe6bc5SPekka Enberg 				cpuid(&regs);
121*a1fe6bc5SPekka Enberg 
122*a1fe6bc5SPekka Enberg 				kvm_cpuid->entries[ndx++]	= (struct kvm_cpuid_entry2) {
123*a1fe6bc5SPekka Enberg 					.function	= function,
124*a1fe6bc5SPekka Enberg 					.index		= 0,
125*a1fe6bc5SPekka Enberg 					.flags		= KVM_CPUID_FLAG_STATEFUL_FUNC,
126*a1fe6bc5SPekka Enberg 					.eax		= regs.eax,
127*a1fe6bc5SPekka Enberg 					.ebx		= regs.ebx,
128*a1fe6bc5SPekka Enberg 					.ecx		= regs.ecx,
129*a1fe6bc5SPekka Enberg 					.edx		= regs.edx,
130*a1fe6bc5SPekka Enberg 				};
131*a1fe6bc5SPekka Enberg 			}
132*a1fe6bc5SPekka Enberg 			break;
133*a1fe6bc5SPekka Enberg 		}
134*a1fe6bc5SPekka Enberg 		case 0x04: {
135*a1fe6bc5SPekka Enberg 			uint32_t index;
136*a1fe6bc5SPekka Enberg 
137*a1fe6bc5SPekka Enberg 			for (index = 0; index < 2; index++) {
138*a1fe6bc5SPekka Enberg 				struct cpuid_regs regs = {
139*a1fe6bc5SPekka Enberg 					.eax		= function,
140*a1fe6bc5SPekka Enberg 					.ecx		= index,
141*a1fe6bc5SPekka Enberg 				};
142*a1fe6bc5SPekka Enberg 				cpuid(&regs);
143*a1fe6bc5SPekka Enberg 
144*a1fe6bc5SPekka Enberg 				kvm_cpuid->entries[ndx++]	= (struct kvm_cpuid_entry2) {
145*a1fe6bc5SPekka Enberg 					.function	= function,
146*a1fe6bc5SPekka Enberg 					.index		= index,
147*a1fe6bc5SPekka Enberg 					.flags		= KVM_CPUID_FLAG_SIGNIFCANT_INDEX,
148*a1fe6bc5SPekka Enberg 					.eax		= regs.eax,
149*a1fe6bc5SPekka Enberg 					.ebx		= regs.ebx,
150*a1fe6bc5SPekka Enberg 					.ecx		= regs.ecx,
151*a1fe6bc5SPekka Enberg 					.edx		= regs.edx,
152*a1fe6bc5SPekka Enberg 				};
153*a1fe6bc5SPekka Enberg 			}
154*a1fe6bc5SPekka Enberg 			break;
155*a1fe6bc5SPekka Enberg 		}
156*a1fe6bc5SPekka Enberg 		default: {
157*a1fe6bc5SPekka Enberg 			struct cpuid_regs regs;
158*a1fe6bc5SPekka Enberg 
159*a1fe6bc5SPekka Enberg 			regs	= (struct cpuid_regs) {
160*a1fe6bc5SPekka Enberg 				.eax		= function,
161*a1fe6bc5SPekka Enberg 			};
162*a1fe6bc5SPekka Enberg 			cpuid(&regs);
163*a1fe6bc5SPekka Enberg 
164*a1fe6bc5SPekka Enberg 			kvm_cpuid->entries[ndx++]	= (struct kvm_cpuid_entry2) {
165*a1fe6bc5SPekka Enberg 				.function	= function,
166*a1fe6bc5SPekka Enberg 				.index		= 0,
167*a1fe6bc5SPekka Enberg 				.flags		= 0,
168*a1fe6bc5SPekka Enberg 				.eax		= regs.eax,
169*a1fe6bc5SPekka Enberg 				.ebx		= regs.ebx,
170*a1fe6bc5SPekka Enberg 				.ecx		= regs.ecx,
171*a1fe6bc5SPekka Enberg 				.edx		= regs.edx,
172*a1fe6bc5SPekka Enberg 			};
173*a1fe6bc5SPekka Enberg 			break;
174*a1fe6bc5SPekka Enberg 		}};
175*a1fe6bc5SPekka Enberg 	}
176*a1fe6bc5SPekka Enberg 
177*a1fe6bc5SPekka Enberg 	highest_ext	= cpuid_highest_ext_func();
178*a1fe6bc5SPekka Enberg 
179*a1fe6bc5SPekka Enberg 	for (function = CPUID_GET_HIGHEST_EXT_FUNCTION; function < highest_ext; function++) {
180*a1fe6bc5SPekka Enberg 		struct cpuid_regs regs;
181*a1fe6bc5SPekka Enberg 
182*a1fe6bc5SPekka Enberg 		regs	= (struct cpuid_regs) {
183*a1fe6bc5SPekka Enberg 			.eax		= function,
184*a1fe6bc5SPekka Enberg 		};
185*a1fe6bc5SPekka Enberg 		cpuid(&regs);
186*a1fe6bc5SPekka Enberg 
187*a1fe6bc5SPekka Enberg 		kvm_cpuid->entries[ndx++]	= (struct kvm_cpuid_entry2) {
188*a1fe6bc5SPekka Enberg 			.function	= function,
189*a1fe6bc5SPekka Enberg 			.index		= 0,
190*a1fe6bc5SPekka Enberg 			.flags		= 0,
191*a1fe6bc5SPekka Enberg 			.eax		= regs.eax,
192*a1fe6bc5SPekka Enberg 			.ebx		= regs.ebx,
193*a1fe6bc5SPekka Enberg 			.ecx		= regs.ecx,
194*a1fe6bc5SPekka Enberg 			.edx		= regs.edx,
195*a1fe6bc5SPekka Enberg 		};
196*a1fe6bc5SPekka Enberg 	}
197*a1fe6bc5SPekka Enberg 
198*a1fe6bc5SPekka Enberg 	assert(ndx < MAX_KVM_CPUID_ENTRIES);
199*a1fe6bc5SPekka Enberg 
200*a1fe6bc5SPekka Enberg 	kvm_cpuid->nent		= ndx;
201*a1fe6bc5SPekka Enberg 
202*a1fe6bc5SPekka Enberg 	if (ioctl(self->vcpu_fd, KVM_SET_CPUID2, kvm_cpuid) < 0)
203*a1fe6bc5SPekka Enberg 		die_perror("KVM_SET_CPUID2 failed");
204*a1fe6bc5SPekka Enberg 
205*a1fe6bc5SPekka Enberg 	kvm_cpuid__delete(kvm_cpuid);
206*a1fe6bc5SPekka Enberg }
207