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.


Context-based output infrastructure (V3_Print, etc) and modifications to use it
[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 X2APIC
55     v3_cpuid_add_fields(vm, 0x00000001, 0, 0, 0, 0, (1 << 21), 0, 0, 0);
56
57
58     // Demarcate machine as a VM
59     v3_cpuid_add_fields(vm, 0x00000001,
60                         0, 0,
61                         0, 0,
62                         0x80000000, 0x80000000,
63                         0, 0
64                         );
65
66
67     // disable ARAT
68     v3_cpuid_add_fields(vm, 0x00000006, (1 << 2), 0, 0, 0, 0, 0, 0, 0);
69
70 }
71
72
73
74
75 int v3_deinit_cpuid_map(struct v3_vm_info * vm) {
76     struct rb_node * node = v3_rb_first(&(vm->cpuid_map.map));
77     struct v3_cpuid_hook * hook = NULL;
78     struct rb_node * tmp_node = NULL;
79     
80
81     while (node) {
82         hook = rb_entry(node, struct v3_cpuid_hook, tree_node);
83         tmp_node = node;
84         node = v3_rb_next(node);
85
86         v3_rb_erase(&(hook->tree_node), &(vm->cpuid_map.map));
87         V3_Free(hook);
88         
89     }
90
91     return 0;
92 }
93
94
95 static inline struct v3_cpuid_hook * __insert_cpuid_hook(struct v3_vm_info * vm, struct v3_cpuid_hook * hook) {
96   struct rb_node ** p = &(vm->cpuid_map.map.rb_node);
97   struct rb_node * parent = NULL;
98   struct v3_cpuid_hook * tmp_hook = NULL;
99
100   while (*p) {
101     parent = *p;
102     tmp_hook = rb_entry(parent, struct v3_cpuid_hook, tree_node);
103
104     if (hook->cpuid < tmp_hook->cpuid) {
105       p = &(*p)->rb_left;
106     } else if (hook->cpuid > tmp_hook->cpuid) {
107       p = &(*p)->rb_right;
108     } else {
109       return tmp_hook;
110     }
111   }
112   rb_link_node(&(hook->tree_node), parent, p);
113
114   return NULL;
115 }
116
117
118 static inline struct v3_cpuid_hook * insert_cpuid_hook(struct v3_vm_info * vm, struct v3_cpuid_hook * hook) {
119   struct v3_cpuid_hook * ret;
120
121   if ((ret = __insert_cpuid_hook(vm, hook))) {
122     return ret;
123   }
124
125   v3_rb_insert_color(&(hook->tree_node), &(vm->cpuid_map.map));
126
127   return NULL;
128 }
129
130
131
132 static struct v3_cpuid_hook * get_cpuid_hook(struct v3_vm_info * vm, uint32_t cpuid) {
133   struct rb_node * n = vm->cpuid_map.map.rb_node;
134   struct v3_cpuid_hook * hook = NULL;
135
136   while (n) {
137     hook = rb_entry(n, struct v3_cpuid_hook, tree_node);
138     
139     if (cpuid < hook->cpuid) {
140       n = n->rb_left;
141     } else if (cpuid > hook->cpuid) {
142       n = n->rb_right;
143     } else {
144       return hook;
145     }
146   }
147
148   return NULL;
149 }
150
151
152
153 static int mask_hook(struct guest_info * core, uint32_t cpuid, 
154               uint32_t * eax, uint32_t * ebx, 
155               uint32_t * ecx, uint32_t * edx,
156               void * priv_data) {
157     struct masked_cpuid * mask = (struct masked_cpuid *)priv_data;
158
159     v3_cpuid(cpuid, eax, ebx, ecx, edx);
160
161     *eax &= ~(mask->rax_mask);
162     *eax |= (mask->rax & mask->rax_mask);
163
164     *ebx &= ~(mask->rbx_mask);
165     *ebx |= (mask->rbx & mask->rbx_mask);
166
167     *ecx &= ~(mask->rcx_mask);
168     *ecx |= (mask->rcx & mask->rcx_mask);
169
170     *edx &= ~(mask->rdx_mask);
171     *edx |= (mask->rdx & mask->rdx_mask);
172
173     return 0;
174 }
175
176
177
178 /* This function allows you to reserve a set of bits in a given cpuid value 
179  * For each cpuid return register you specify which bits you want to reserve in the mask.
180  * The value of those bits is set in the reg param.
181  * The values of the reserved bits are  returned to the guest, when it reads the cpuid
182  */ 
183 int v3_cpuid_add_fields(struct v3_vm_info * vm, uint32_t cpuid, 
184                         uint32_t rax_mask, uint32_t rax,
185                         uint32_t rbx_mask, uint32_t rbx, 
186                         uint32_t rcx_mask, uint32_t rcx, 
187                         uint32_t rdx_mask, uint32_t rdx) {
188     struct v3_cpuid_hook * hook = get_cpuid_hook(vm, cpuid);
189
190
191     if ((~rax_mask & rax) || (~rbx_mask & rbx) ||
192         (~rcx_mask & rcx) || (~rdx_mask & rdx)) {
193         PrintError(vm, VCORE_NONE, "Invalid cpuid reg value (mask overrun)\n");
194         return -1;
195     }
196
197
198     if (hook == NULL) {
199         struct masked_cpuid * mask = V3_Malloc(sizeof(struct masked_cpuid));
200
201         if (!mask) {
202             PrintError(vm, VCORE_NONE, "Unable to alocate space for cpu id mask\n");
203             return -1;
204         }
205
206         memset(mask, 0, sizeof(struct masked_cpuid));
207         
208         mask->rax_mask = rax_mask;
209         mask->rax = rax;
210         mask->rbx_mask = rbx_mask;
211         mask->rbx = rbx;
212         mask->rcx_mask = rcx_mask;
213         mask->rcx = rcx;
214         mask->rdx_mask = rdx_mask;
215         mask->rdx = rdx;
216
217         if (v3_hook_cpuid(vm, cpuid, mask_hook, mask) == -1) {
218             PrintError(vm, VCORE_NONE, "Error hooking cpuid %d\n", cpuid);
219             V3_Free(mask);
220             return -1;
221         }
222     } else {
223         struct masked_cpuid * mask = NULL;
224         uint32_t tmp_val = 0;
225
226         if (hook->hook_fn != mask_hook) {
227             PrintError(vm, VCORE_NONE, "trying to add fields to a fully hooked cpuid (%d)\n", cpuid);
228             return -1;
229         }
230         
231         mask = (struct masked_cpuid *)(hook->private_data);
232
233         if ((mask->rax_mask & rax_mask) ||
234             (mask->rbx_mask & rbx_mask) || 
235             (mask->rcx_mask & rcx_mask) || 
236             (mask->rdx_mask & rdx_mask)) {
237             PrintError(vm, VCORE_NONE, "Trying to add fields that have already been masked\n");
238             return -1;
239         }
240
241         mask->rax_mask |= rax_mask;
242         mask->rbx_mask |= rbx_mask;
243         mask->rcx_mask |= rcx_mask;
244         mask->rdx_mask |= rdx_mask;
245         
246         mask->rax |= rax;
247         tmp_val = (~rax_mask | rax);
248         mask->rax &= tmp_val;
249
250         mask->rbx |= rbx;
251         tmp_val = (~rbx_mask | rbx);
252         mask->rbx &= tmp_val;
253
254         mask->rcx |= rcx;
255         tmp_val = (~rcx_mask | rcx);
256         mask->rcx &= tmp_val;
257
258         mask->rdx |= rdx;
259         tmp_val = (~rdx_mask | rdx);
260         mask->rdx &= tmp_val;
261
262     }
263
264     return 0;
265 }
266
267 int v3_unhook_cpuid(struct v3_vm_info * vm, uint32_t cpuid) {
268     struct v3_cpuid_hook * hook = get_cpuid_hook(vm, cpuid);
269
270     if (hook == NULL) {
271         PrintError(vm, VCORE_NONE, "Could not find cpuid to unhook (0x%x)\n", cpuid);
272         return -1;
273     }
274
275     v3_rb_erase(&(hook->tree_node), &(vm->cpuid_map.map));
276
277     V3_Free(hook);
278
279     return 0;
280 }
281
282 int v3_hook_cpuid(struct v3_vm_info * vm, uint32_t cpuid, 
283                   int (*hook_fn)(struct guest_info * info, uint32_t cpuid, \
284                                  uint32_t * eax, uint32_t * ebx, \
285                                  uint32_t * ecx, uint32_t * edx, \
286                                  void * private_data), 
287                   void * private_data) {
288     struct v3_cpuid_hook * hook = NULL;
289
290     if (hook_fn == NULL) {
291         PrintError(vm, VCORE_NONE, "CPUID hook requested with null handler\n");
292         return -1;
293     }
294
295     hook = (struct v3_cpuid_hook *)V3_Malloc(sizeof(struct v3_cpuid_hook));
296
297     if (!hook) {
298         PrintError(vm, VCORE_NONE, "Cannot allocate memory to hook cpu id\n");
299         return -1;
300     }
301
302     hook->cpuid = cpuid;
303     hook->private_data = private_data;
304     hook->hook_fn = hook_fn;
305
306     if (insert_cpuid_hook(vm, hook)) {
307         PrintError(vm, VCORE_NONE, "Could not hook cpuid 0x%x (already hooked)\n", cpuid);
308         V3_Free(hook);
309         return -1;
310     }
311
312     return 0;
313 }
314
315 int v3_handle_cpuid(struct guest_info * info) {
316     uint32_t cpuid = info->vm_regs.rax;
317     struct v3_cpuid_hook * hook = get_cpuid_hook(info->vm_info, cpuid);
318
319     //PrintDebug(info->vm_info, info, "CPUID called for 0x%x\n", cpuid);
320
321     if (hook == NULL) {
322         //PrintDebug(info->vm_info, info, "Calling passthrough handler\n");
323         // call the passthrough handler
324         v3_cpuid(cpuid, 
325                  (uint32_t *)&(info->vm_regs.rax), 
326                  (uint32_t *)&(info->vm_regs.rbx), 
327                  (uint32_t *)&(info->vm_regs.rcx), 
328                  (uint32_t *)&(info->vm_regs.rdx));
329     } else {
330         //      PrintDebug(info->vm_info, info, "Calling hook function\n");
331
332         if (hook->hook_fn(info, cpuid, 
333                           (uint32_t *)&(info->vm_regs.rax), 
334                           (uint32_t *)&(info->vm_regs.rbx), 
335                           (uint32_t *)&(info->vm_regs.rcx), 
336                           (uint32_t *)&(info->vm_regs.rdx), 
337                           hook->private_data) == -1) {
338             PrintError(info->vm_info, info, "Error in cpuid handler for 0x%x\n", cpuid);
339             return -1;
340         }
341     }
342
343     //    PrintDebug(info->vm_info, info, "Cleaning up register contents\n");
344
345     info->vm_regs.rax &= 0x00000000ffffffffLL;
346     info->vm_regs.rbx &= 0x00000000ffffffffLL;
347     info->vm_regs.rcx &= 0x00000000ffffffffLL;
348     info->vm_regs.rdx &= 0x00000000ffffffffLL;
349
350     info->rip += 2;
351
352     return 0;
353 }
354
355
356
357
358