Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


fb22979e0cdbe057b321f720b472238f881d06eb
[palacios.git] / palacios / src / palacios / vmm_cpuid.c
1 /* 
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2011, Jack Lange <jacklange@cs.pitt.edu> 
11  * All rights reserved.
12  *
13  * Author: Jack Lange <jacklange@cs.pitt.edu>
14  *
15  * This is free software.  You are permitted to use,
16  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
17  */
18
19 #include <palacios/vmm.h>
20 #include <palacios/vmm_cpuid.h>
21 #include <palacios/vmm_lowlevel.h>
22 #include <palacios/vm_guest.h>
23
24 struct masked_cpuid {
25     uint32_t rax_mask;
26     uint32_t rbx_mask;
27     uint32_t rcx_mask;
28     uint32_t rdx_mask;
29
30     uint32_t rax;
31     uint32_t rbx;
32     uint32_t rcx;
33     uint32_t rdx;
34 };
35
36
37 void v3_init_cpuid_map(struct v3_vm_info * vm) {
38     vm->cpuid_map.map.rb_node = NULL;
39
40     // Setup default cpuid entries
41
42
43     // Disable XSAVE (cpuid 0x01, ECX bit 26)
44     v3_cpuid_add_fields(vm, 0x01, 0, 0, 0, 0, (1 << 26), 0, 0, 0);
45
46     // Disable MONITOR/MWAIT (cpuid 0x01, ECX bit 3)
47     v3_cpuid_add_fields(vm, 0x01, 0, 0, 0, 0, (1 << 3), 0, 0, 0);
48
49
50     // disable MTRR
51     v3_cpuid_add_fields(vm, 0x00000001, 0, 0, 0, 0, 0, 0, (1 << 12), 0);
52     // disable PAT
53     v3_cpuid_add_fields(vm, 0x00000001, 0, 0, 0, 0, 0, 0, (1 << 16), 0);
54     // disable HTT
55     v3_cpuid_add_fields(vm, 0x00000001, 0, 0, 0, 0, 0, 0, (1 << 28), 0);
56
57     // disable X2APIC
58     v3_cpuid_add_fields(vm, 0x00000001, 0, 0, 0, 0, (1 << 21), 0, 0, 0);
59
60
61     // Demarcate machine as a VM
62     v3_cpuid_add_fields(vm, 0x00000001,
63                         0, 0,
64                         0, 0,
65                         0x80000000, 0x80000000,
66                         0, 0
67                         );
68
69
70     // disable ARAT
71     v3_cpuid_add_fields(vm, 0x00000006, (1 << 2), 0, 0, 0, 0, 0, 0, 0);
72
73 }
74
75
76
77
78 int v3_deinit_cpuid_map(struct v3_vm_info * vm) {
79     struct rb_node * node = v3_rb_first(&(vm->cpuid_map.map));
80     struct v3_cpuid_hook * hook = NULL;
81     struct rb_node * tmp_node = NULL;
82     
83
84     while (node) {
85         hook = rb_entry(node, struct v3_cpuid_hook, tree_node);
86         tmp_node = node;
87         node = v3_rb_next(node);
88
89         v3_rb_erase(&(hook->tree_node), &(vm->cpuid_map.map));
90         V3_Free(hook);
91         
92     }
93
94     return 0;
95 }
96
97
98 static inline struct v3_cpuid_hook * __insert_cpuid_hook(struct v3_vm_info * vm, struct v3_cpuid_hook * hook) {
99   struct rb_node ** p = &(vm->cpuid_map.map.rb_node);
100   struct rb_node * parent = NULL;
101   struct v3_cpuid_hook * tmp_hook = NULL;
102
103   while (*p) {
104     parent = *p;
105     tmp_hook = rb_entry(parent, struct v3_cpuid_hook, tree_node);
106
107     if (hook->cpuid < tmp_hook->cpuid) {
108       p = &(*p)->rb_left;
109     } else if (hook->cpuid > tmp_hook->cpuid) {
110       p = &(*p)->rb_right;
111     } else {
112       return tmp_hook;
113     }
114   }
115   rb_link_node(&(hook->tree_node), parent, p);
116
117   return NULL;
118 }
119
120
121 static inline struct v3_cpuid_hook * insert_cpuid_hook(struct v3_vm_info * vm, struct v3_cpuid_hook * hook) {
122   struct v3_cpuid_hook * ret;
123
124   if ((ret = __insert_cpuid_hook(vm, hook))) {
125     return ret;
126   }
127
128   v3_rb_insert_color(&(hook->tree_node), &(vm->cpuid_map.map));
129
130   return NULL;
131 }
132
133
134
135 static struct v3_cpuid_hook * get_cpuid_hook(struct v3_vm_info * vm, uint32_t cpuid) {
136   struct rb_node * n = vm->cpuid_map.map.rb_node;
137   struct v3_cpuid_hook * hook = NULL;
138
139   while (n) {
140     hook = rb_entry(n, struct v3_cpuid_hook, tree_node);
141     
142     if (cpuid < hook->cpuid) {
143       n = n->rb_left;
144     } else if (cpuid > hook->cpuid) {
145       n = n->rb_right;
146     } else {
147       return hook;
148     }
149   }
150
151   return NULL;
152 }
153
154
155
156 static int mask_hook(struct guest_info * core, uint32_t cpuid, 
157               uint32_t * eax, uint32_t * ebx, 
158               uint32_t * ecx, uint32_t * edx,
159               void * priv_data) {
160     struct masked_cpuid * mask = (struct masked_cpuid *)priv_data;
161
162     v3_cpuid(cpuid, eax, ebx, ecx, edx);
163
164     *eax &= ~(mask->rax_mask);
165     *eax |= (mask->rax & mask->rax_mask);
166
167     *ebx &= ~(mask->rbx_mask);
168     *ebx |= (mask->rbx & mask->rbx_mask);
169
170     *ecx &= ~(mask->rcx_mask);
171     *ecx |= (mask->rcx & mask->rcx_mask);
172
173     *edx &= ~(mask->rdx_mask);
174     *edx |= (mask->rdx & mask->rdx_mask);
175
176     return 0;
177 }
178
179
180
181 /* This function allows you to reserve a set of bits in a given cpuid value 
182  * For each cpuid return register you specify which bits you want to reserve in the mask.
183  * The value of those bits is set in the reg param.
184  * The values of the reserved bits are  returned to the guest, when it reads the cpuid
185  */ 
186 int v3_cpuid_add_fields(struct v3_vm_info * vm, uint32_t cpuid, 
187                         uint32_t rax_mask, uint32_t rax,
188                         uint32_t rbx_mask, uint32_t rbx, 
189                         uint32_t rcx_mask, uint32_t rcx, 
190                         uint32_t rdx_mask, uint32_t rdx) {
191     struct v3_cpuid_hook * hook = get_cpuid_hook(vm, cpuid);
192
193
194     if ((~rax_mask & rax) || (~rbx_mask & rbx) ||
195         (~rcx_mask & rcx) || (~rdx_mask & rdx)) {
196         PrintError(vm, VCORE_NONE, "Invalid cpuid reg value (mask overrun)\n");
197         return -1;
198     }
199
200
201     if (hook == NULL) {
202         struct masked_cpuid * mask = V3_Malloc(sizeof(struct masked_cpuid));
203
204         if (!mask) {
205             PrintError(vm, VCORE_NONE, "Unable to alocate space for cpu id mask\n");
206             return -1;
207         }
208
209         memset(mask, 0, sizeof(struct masked_cpuid));
210         
211         mask->rax_mask = rax_mask;
212         mask->rax = rax;
213         mask->rbx_mask = rbx_mask;
214         mask->rbx = rbx;
215         mask->rcx_mask = rcx_mask;
216         mask->rcx = rcx;
217         mask->rdx_mask = rdx_mask;
218         mask->rdx = rdx;
219
220         if (v3_hook_cpuid(vm, cpuid, mask_hook, mask) == -1) {
221             PrintError(vm, VCORE_NONE, "Error hooking cpuid %d\n", cpuid);
222             V3_Free(mask);
223             return -1;
224         }
225     } else {
226         struct masked_cpuid * mask = NULL;
227         uint32_t tmp_val = 0;
228
229         if (hook->hook_fn != mask_hook) {
230             PrintError(vm, VCORE_NONE, "trying to add fields to a fully hooked cpuid (%d)\n", cpuid);
231             return -1;
232         }
233         
234         mask = (struct masked_cpuid *)(hook->private_data);
235
236         if ((mask->rax_mask & rax_mask) ||
237             (mask->rbx_mask & rbx_mask) || 
238             (mask->rcx_mask & rcx_mask) || 
239             (mask->rdx_mask & rdx_mask)) {
240             PrintError(vm, VCORE_NONE, "Trying to add fields that have already been masked\n");
241             return -1;
242         }
243
244         mask->rax_mask |= rax_mask;
245         mask->rbx_mask |= rbx_mask;
246         mask->rcx_mask |= rcx_mask;
247         mask->rdx_mask |= rdx_mask;
248         
249         mask->rax |= rax;
250         tmp_val = (~rax_mask | rax);
251         mask->rax &= tmp_val;
252
253         mask->rbx |= rbx;
254         tmp_val = (~rbx_mask | rbx);
255         mask->rbx &= tmp_val;
256
257         mask->rcx |= rcx;
258         tmp_val = (~rcx_mask | rcx);
259         mask->rcx &= tmp_val;
260
261         mask->rdx |= rdx;
262         tmp_val = (~rdx_mask | rdx);
263         mask->rdx &= tmp_val;
264
265     }
266
267     return 0;
268 }
269
270 int v3_unhook_cpuid(struct v3_vm_info * vm, uint32_t cpuid) {
271     struct v3_cpuid_hook * hook = get_cpuid_hook(vm, cpuid);
272
273     if (hook == NULL) {
274         PrintError(vm, VCORE_NONE, "Could not find cpuid to unhook (0x%x)\n", cpuid);
275         return -1;
276     }
277
278     v3_rb_erase(&(hook->tree_node), &(vm->cpuid_map.map));
279
280     V3_Free(hook);
281
282     return 0;
283 }
284
285 int v3_hook_cpuid(struct v3_vm_info * vm, uint32_t cpuid, 
286                   int (*hook_fn)(struct guest_info * info, uint32_t cpuid, \
287                                  uint32_t * eax, uint32_t * ebx, \
288                                  uint32_t * ecx, uint32_t * edx, \
289                                  void * private_data), 
290                   void * private_data) {
291     struct v3_cpuid_hook * hook = NULL;
292
293     if (hook_fn == NULL) {
294         PrintError(vm, VCORE_NONE, "CPUID hook requested with null handler\n");
295         return -1;
296     }
297
298     hook = (struct v3_cpuid_hook *)V3_Malloc(sizeof(struct v3_cpuid_hook));
299
300     if (!hook) {
301         PrintError(vm, VCORE_NONE, "Cannot allocate memory to hook cpu id\n");
302         return -1;
303     }
304
305     hook->cpuid = cpuid;
306     hook->private_data = private_data;
307     hook->hook_fn = hook_fn;
308
309     if (insert_cpuid_hook(vm, hook)) {
310         PrintError(vm, VCORE_NONE, "Could not hook cpuid 0x%x (already hooked)\n", cpuid);
311         V3_Free(hook);
312         return -1;
313     }
314
315     return 0;
316 }
317
318 int v3_handle_cpuid(struct guest_info * info) {
319     uint32_t cpuid = info->vm_regs.rax;
320     struct v3_cpuid_hook * hook = get_cpuid_hook(info->vm_info, cpuid);
321
322     //PrintDebug(info->vm_info, info, "CPUID called for 0x%x\n", cpuid);
323
324     if (hook == NULL) {
325         //PrintDebug(info->vm_info, info, "Calling passthrough handler\n");
326         // call the passthrough handler
327         v3_cpuid(cpuid, 
328                  (uint32_t *)&(info->vm_regs.rax), 
329                  (uint32_t *)&(info->vm_regs.rbx), 
330                  (uint32_t *)&(info->vm_regs.rcx), 
331                  (uint32_t *)&(info->vm_regs.rdx));
332     } else {
333         //      PrintDebug(info->vm_info, info, "Calling hook function\n");
334
335         if (hook->hook_fn(info, cpuid, 
336                           (uint32_t *)&(info->vm_regs.rax), 
337                           (uint32_t *)&(info->vm_regs.rbx), 
338                           (uint32_t *)&(info->vm_regs.rcx), 
339                           (uint32_t *)&(info->vm_regs.rdx), 
340                           hook->private_data) == -1) {
341             PrintError(info->vm_info, info, "Error in cpuid handler for 0x%x\n", cpuid);
342             return -1;
343         }
344     }
345
346     //    PrintDebug(info->vm_info, info, "Cleaning up register contents\n");
347
348     info->vm_regs.rax &= 0x00000000ffffffffLL;
349     info->vm_regs.rbx &= 0x00000000ffffffffLL;
350     info->vm_regs.rcx &= 0x00000000ffffffffLL;
351     info->vm_regs.rdx &= 0x00000000ffffffffLL;
352
353     info->rip += 2;
354
355     return 0;
356 }
357
358
359
360
361