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.


ed78fe15576972877025af5baf2a75cd5ecdc560
[palacios.git] / palacios / src / geekos / svm.c
1 #include <geekos/svm.h>
2 #include <geekos/vmm.h>
3
4 #include <geekos/vmcb.h>
5 #include <geekos/vmm_mem.h>
6 #include <geekos/vmm_paging.h>
7 #include <geekos/svm_handler.h>
8
9 #include <geekos/vmm_debug.h>
10
11
12 /* TEMPORARY BECAUSE SVM IS WEIRD */
13 #include <geekos/tss.h>
14 /* ** */
15
16 extern struct vmm_os_hooks * os_hooks;
17
18 extern uint_t cpuid_ecx(uint_t op);
19 extern uint_t cpuid_edx(uint_t op);
20 extern void Get_MSR(uint_t MSR, uint_t * high_byte, uint_t * low_byte); 
21 extern void Set_MSR(uint_t MSR, uint_t high_byte, uint_t low_byte);
22 extern uint_t launch_svm(vmcb_t * vmcb_addr);
23 extern void safe_svm_launch(vmcb_t * vmcb_addr, struct guest_gprs * gprs);
24
25 extern uint_t Get_CR3();
26
27 extern void GetGDTR(void * gdt);
28 extern void GetIDTR(void * idt);
29
30 extern void DisableInts();
31
32 /* Checks machine SVM capability */
33 /* Implemented from: AMD Arch Manual 3, sect 15.4 */ 
34 int is_svm_capable() {
35   uint_t ret =  cpuid_ecx(CPUID_FEATURE_IDS);
36   uint_t vm_cr_low = 0, vm_cr_high = 0;
37
38
39   if ((ret & CPUID_FEATURE_IDS_ecx_svm_avail) == 0) {
40     PrintDebug("SVM Not Available\n");
41     return 0;
42   } 
43
44   Get_MSR(SVM_VM_CR_MSR, &vm_cr_high, &vm_cr_low);
45
46   if ((vm_cr_low & SVM_VM_CR_MSR_svmdis) == 0) {
47     return 1;
48   }
49
50   ret = cpuid_edx(CPUID_SVM_REV_AND_FEATURE_IDS);
51   
52
53   if ((ret & CPUID_SVM_REV_AND_FEATURE_IDS_edx_np) == 0) {
54     PrintDebug("Nested Paging not supported\n");
55   }
56
57   if ((ret & CPUID_SVM_REV_AND_FEATURE_IDS_edx_svml) == 0) {
58     PrintDebug("SVM BIOS Disabled, not unlockable\n");
59   } else {
60     PrintDebug("SVM is locked with a key\n");
61   }
62
63   return 0;
64 }
65
66
67
68 void Init_SVM(struct vmm_ctrl_ops * vmm_ops) {
69   reg_ex_t msr;
70   void * host_state;
71
72
73   // Enable SVM on the CPU
74   Get_MSR(EFER_MSR, &(msr.e_reg.high), &(msr.e_reg.low));
75   msr.e_reg.low |= EFER_MSR_svm_enable;
76   Set_MSR(EFER_MSR, 0, msr.e_reg.low);
77   
78   PrintDebug("SVM Enabled\n");
79
80
81   // Setup the host state save area
82   host_state = os_hooks->allocate_pages(4);
83   
84   msr.e_reg.high = 0;
85   msr.e_reg.low = (uint_t)host_state;
86
87
88   PrintDebug("Host State being saved at %x\n", (uint_t)host_state);
89   Set_MSR(SVM_VM_HSAVE_PA_MSR, msr.e_reg.high, msr.e_reg.low);
90
91
92
93   // Setup the SVM specific vmm operations
94   vmm_ops->init_guest = &init_svm_guest;
95   vmm_ops->start_guest = &start_svm_guest;
96
97
98   return;
99 }
100
101
102 int init_svm_guest(struct guest_info *info) {
103  
104   PrintDebug("Allocating VMCB\n");
105   info->vmm_data = (void*)Allocate_VMCB();
106
107
108   PrintDebug("Generating Guest nested page tables\n");
109   print_mem_list(&(info->mem_list));
110   print_mem_layout(&(info->mem_layout));
111   info->page_tables = NULL;
112   //info->page_tables = generate_guest_page_tables_64(&(info->mem_layout), &(info->mem_list));
113   info->page_tables = generate_guest_page_tables(&(info->mem_layout), &(info->mem_list));
114   PrintDebugPageTables(info->page_tables);
115
116   
117
118   PrintDebug("Initializing VMCB (addr=%x)\n", info->vmm_data);
119   Init_VMCB((vmcb_t*)(info->vmm_data), *info);
120   
121   
122   info->vm_regs.rbx = 0;
123   info->vm_regs.rcx = 0;
124   info->vm_regs.rdx = 0;
125   info->vm_regs.rsi = 0;
126   info->vm_regs.rdi = 0;
127   info->vm_regs.rbp = 0;
128
129   return 0;
130 }
131
132
133 // can we start a kernel thread here...
134 int start_svm_guest(struct guest_info *info) {
135
136
137
138   PrintDebug("Launching SVM VM (vmcb=%x)\n", info->vmm_data);
139   //PrintDebugVMCB((vmcb_t*)(info->vmm_data));
140
141   while (1) {
142
143     safe_svm_launch((vmcb_t*)(info->vmm_data), &(info->vm_regs));
144     //launch_svm((vmcb_t*)(info->vmm_data));
145     PrintDebug("SVM Returned\n");
146
147     if (handle_svm_exit(info) != 0) {
148       break;
149     }
150   }
151   return 0;
152 }
153
154
155
156 vmcb_t * Allocate_VMCB() {
157   vmcb_t * vmcb_page = (vmcb_t*)os_hooks->allocate_pages(1);
158
159
160   memset(vmcb_page, 0, 4096);
161
162   return vmcb_page;
163 }
164
165
166
167 void Init_VMCB(vmcb_t * vmcb, guest_info_t vm_info) {
168   vmcb_ctrl_t * ctrl_area = GET_VMCB_CTRL_AREA(vmcb);
169   vmcb_saved_state_t * guest_state = GET_VMCB_SAVE_STATE_AREA(vmcb);
170   uint_t i;
171
172
173   guest_state->rsp = vm_info.rsp;
174   guest_state->rip = vm_info.rip;
175
176
177
178   ctrl_area->cr_writes.crs.cr0 = 1;
179
180   guest_state->efer |= EFER_MSR_svm_enable;
181   guest_state->rflags = 0x00000002; // The reserved bit is always 1
182   ctrl_area->svm_instrs.instrs.VMRUN = 1;
183   // guest_state->cr0 = 0x00000001;    // PE 
184   ctrl_area->guest_ASID = 1;
185
186
187   ctrl_area->exceptions.ex_names.de = 1;
188   ctrl_area->exceptions.ex_names.df = 1;
189   ctrl_area->exceptions.ex_names.pf = 1;
190   ctrl_area->exceptions.ex_names.ts = 1;
191   ctrl_area->exceptions.ex_names.ss = 1;
192   ctrl_area->exceptions.ex_names.ac = 1;
193   ctrl_area->exceptions.ex_names.mc = 1;
194   ctrl_area->exceptions.ex_names.gp = 1;
195   ctrl_area->exceptions.ex_names.ud = 1;
196   ctrl_area->exceptions.ex_names.np = 1;
197   ctrl_area->exceptions.ex_names.of = 1;
198   ctrl_area->exceptions.ex_names.nmi = 1;
199
200   guest_state->cs.selector = 0x0000;
201   guest_state->cs.limit=~0u;
202   guest_state->cs.base = guest_state->cs.selector<<4;
203   guest_state->cs.attrib.raw = 0xf3;
204
205   
206   struct vmcb_selector *segregs [] = {&(guest_state->ss), &(guest_state->ds), &(guest_state->es), &(guest_state->fs), &(guest_state->gs), NULL};
207   for ( i = 0; segregs[i] != NULL; i++) {
208     struct vmcb_selector * seg = segregs[i];
209     
210     seg->selector = 0x0000;
211     seg->base = seg->selector << 4;
212     seg->attrib.raw = 0xf3;
213     seg->limit = ~0u;
214   }
215   
216   if (vm_info.io_map.num_ports > 0) {
217     vmm_io_hook_t * iter;
218     addr_t io_port_bitmap;
219     
220     io_port_bitmap = (addr_t)os_hooks->allocate_pages(3);
221     memset((uchar_t*)io_port_bitmap, 0, PAGE_SIZE * 3);
222     
223     ctrl_area->IOPM_BASE_PA = io_port_bitmap;
224
225     //PrintDebug("Setting up IO Map at 0x%x\n", io_port_bitmap);
226
227     FOREACH_IO_HOOK(vm_info.io_map, iter) {
228       ushort_t port = iter->port;
229       uchar_t * bitmap = (uchar_t *)io_port_bitmap;
230
231       bitmap += (port / 8);
232       PrintDebug("Setting Bit in block %x\n", bitmap);
233       *bitmap |= 1 << (port % 8);
234     }
235
236     memset((uchar_t*)io_port_bitmap, 0xff, PAGE_SIZE * 2);
237     //PrintDebugMemDump((uchar_t*)io_port_bitmap, PAGE_SIZE *2);
238
239     ctrl_area->instrs.instrs.IOIO_PROT = 1;
240   }
241
242   ctrl_area->instrs.instrs.INTR = 1;
243
244   // also determine if CPU supports nested paging
245   if (vm_info.page_tables) {
246     //   if (0) {
247     // Flush the TLB on entries/exits
248     //ctrl_area->TLB_CONTROL = 1;
249
250     // Enable Nested Paging
251     //ctrl_area->NP_ENABLE = 1;
252
253     //PrintDebug("NP_Enable at 0x%x\n", &(ctrl_area->NP_ENABLE));
254
255         // Set the Nested Page Table pointer
256     //    ctrl_area->N_CR3 = ((addr_t)vm_info.page_tables);
257     ctrl_area->N_CR3 = 0;
258     guest_state->cr3 = (addr_t)(vm_info.page_tables);
259
260     //   ctrl_area->N_CR3 = Get_CR3();
261     // guest_state->cr3 |= (Get_CR3() & 0xfffff000);
262
263     guest_state->g_pat = 0x7040600070406ULL;
264
265     //PrintDebug("Set Nested CR3: lo: 0x%x  hi: 0x%x\n", (uint_t)*(&(ctrl_area->N_CR3)), (uint_t)*((unsigned char *)&(ctrl_area->N_CR3) + 4));
266   guest_state->cr0 |= 0x80000000;
267   }
268
269
270
271 }
272
273 void Init_VMCB_pe(vmcb_t *vmcb, guest_info_t vm_info) {
274   vmcb_ctrl_t * ctrl_area = GET_VMCB_CTRL_AREA(vmcb);
275   vmcb_saved_state_t * guest_state = GET_VMCB_SAVE_STATE_AREA(vmcb);
276   uint_t i = 0;
277
278
279   guest_state->rsp = vm_info.rsp;
280   guest_state->rip = vm_info.rip;
281
282
283   /* I pretty much just gutted this from TVMM */
284   /* Note: That means its probably wrong */
285
286   // set the segment registers to mirror ours
287   guest_state->cs.selector = 1<<3;
288   guest_state->cs.attrib.fields.type = 0xa; // Code segment+read
289   guest_state->cs.attrib.fields.S = 1;
290   guest_state->cs.attrib.fields.P = 1;
291   guest_state->cs.attrib.fields.db = 1;
292   guest_state->cs.attrib.fields.G = 1;
293   guest_state->cs.limit = 0xfffff;
294   guest_state->cs.base = 0;
295   
296   struct vmcb_selector *segregs [] = {&(guest_state->ss), &(guest_state->ds), &(guest_state->es), &(guest_state->fs), &(guest_state->gs), NULL};
297   for ( i = 0; segregs[i] != NULL; i++) {
298     struct vmcb_selector * seg = segregs[i];
299     
300     seg->selector = 2<<3;
301     seg->attrib.fields.type = 0x2; // Data Segment+read/write
302     seg->attrib.fields.S = 1;
303     seg->attrib.fields.P = 1;
304     seg->attrib.fields.db = 1;
305     seg->attrib.fields.G = 1;
306     seg->limit = 0xfffff;
307     seg->base = 0;
308   }
309
310
311   {
312     /* JRL THIS HAS TO GO */
313     
314     guest_state->tr.selector = GetTR_Selector();
315     guest_state->tr.attrib.fields.type = 0x9; 
316     guest_state->tr.attrib.fields.P = 1;
317     guest_state->tr.limit = GetTR_Limit();
318     guest_state->tr.base = GetTR_Base();// - 0x2000;
319     /* ** */
320   }
321
322
323   /* ** */
324
325
326   guest_state->efer |= EFER_MSR_svm_enable;
327   guest_state->rflags = 0x00000002; // The reserved bit is always 1
328   ctrl_area->svm_instrs.instrs.VMRUN = 1;
329   guest_state->cr0 = 0x00000001;    // PE 
330   ctrl_area->guest_ASID = 1;
331
332
333   //  guest_state->cpl = 0;
334
335
336
337   // Setup exits
338
339   ctrl_area->cr_writes.crs.cr4 = 1;
340   
341   ctrl_area->exceptions.ex_names.de = 1;
342   ctrl_area->exceptions.ex_names.df = 1;
343   ctrl_area->exceptions.ex_names.pf = 1;
344   ctrl_area->exceptions.ex_names.ts = 1;
345   ctrl_area->exceptions.ex_names.ss = 1;
346   ctrl_area->exceptions.ex_names.ac = 1;
347   ctrl_area->exceptions.ex_names.mc = 1;
348   ctrl_area->exceptions.ex_names.gp = 1;
349   ctrl_area->exceptions.ex_names.ud = 1;
350   ctrl_area->exceptions.ex_names.np = 1;
351   ctrl_area->exceptions.ex_names.of = 1;
352   ctrl_area->exceptions.ex_names.nmi = 1;
353
354   
355
356   ctrl_area->instrs.instrs.IOIO_PROT = 1;
357   ctrl_area->IOPM_BASE_PA = (uint_t)os_hooks->allocate_pages(3);
358   
359   {
360     reg_ex_t tmp_reg;
361     tmp_reg.r_reg = ctrl_area->IOPM_BASE_PA;
362     memset((void*)(tmp_reg.e_reg.low), 0xffffffff, PAGE_SIZE * 2);
363   }
364
365   ctrl_area->instrs.instrs.INTR = 1;
366
367   
368   {
369     char gdt_buf[6];
370     char idt_buf[6];
371
372     memset(gdt_buf, 0, 6);
373     memset(idt_buf, 0, 6);
374
375
376     uint_t gdt_base, idt_base;
377     ushort_t gdt_limit, idt_limit;
378     
379     GetGDTR(gdt_buf);
380     gdt_base = *(ulong_t*)((uchar_t*)gdt_buf + 2) & 0xffffffff;
381     gdt_limit = *(ushort_t*)(gdt_buf) & 0xffff;
382     PrintDebug("GDT: base: %x, limit: %x\n", gdt_base, gdt_limit);
383
384     GetIDTR(idt_buf);
385     idt_base = *(ulong_t*)(idt_buf + 2) & 0xffffffff;
386     idt_limit = *(ushort_t*)(idt_buf) & 0xffff;
387     PrintDebug("IDT: base: %x, limit: %x\n",idt_base, idt_limit);
388
389
390     // gdt_base -= 0x2000;
391     //idt_base -= 0x2000;
392
393     guest_state->gdtr.base = gdt_base;
394     guest_state->gdtr.limit = gdt_limit;
395     guest_state->idtr.base = idt_base;
396     guest_state->idtr.limit = idt_limit;
397
398
399   }
400   
401
402   // also determine if CPU supports nested paging
403   if (vm_info.page_tables) {
404     //   if (0) {
405     // Flush the TLB on entries/exits
406     ctrl_area->TLB_CONTROL = 1;
407
408     // Enable Nested Paging
409     ctrl_area->NP_ENABLE = 1;
410
411     PrintDebug("NP_Enable at 0x%x\n", &(ctrl_area->NP_ENABLE));
412
413         // Set the Nested Page Table pointer
414     ctrl_area->N_CR3 |= ((addr_t)vm_info.page_tables & 0xfffff000);
415
416
417     //   ctrl_area->N_CR3 = Get_CR3();
418     // guest_state->cr3 |= (Get_CR3() & 0xfffff000);
419
420     guest_state->g_pat = 0x7040600070406ULL;
421
422     PrintDebug("Set Nested CR3: lo: 0x%x  hi: 0x%x\n", (uint_t)*(&(ctrl_area->N_CR3)), (uint_t)*((unsigned char *)&(ctrl_area->N_CR3) + 4));
423     PrintDebug("Set Guest CR3: lo: 0x%x  hi: 0x%x\n", (uint_t)*(&(guest_state->cr3)), (uint_t)*((unsigned char *)&(guest_state->cr3) + 4));
424     // Enable Paging
425     //    guest_state->cr0 |= 0x80000000;
426   }
427
428
429 }
430
431