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.


a3d9c894aa5db4dcbfd92957a5d9a104ef2945ed
[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 void Init_VMCB_Real(vmcb_t * vmcb, guest_info_t vm_info) {
167   vmcb_ctrl_t * ctrl_area = GET_VMCB_CTRL_AREA(vmcb);
168   vmcb_saved_state_t * guest_state = GET_VMCB_SAVE_STATE_AREA(vmcb);
169   uint_t i;
170
171
172   guest_state->rsp = vm_info.rsp;
173   guest_state->rip = vm_info.rip;
174
175
176
177
178
179   guest_state->efer |= EFER_MSR_svm_enable;
180   guest_state->rflags = 0x00000002; // The reserved bit is always 1
181   ctrl_area->svm_instrs.instrs.VMRUN = 1;
182   // guest_state->cr0 = 0x00000001;    // PE 
183   ctrl_area->guest_ASID = 1;
184   guest_state->cr0 = 0x60000010;
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 = 0xf000;
201   guest_state->cs.limit=0xffff;
202   guest_state->cs.base =  0xffff0000;
203   guest_state->cs.attrib.raw = 0x9a;
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 = 0xffff0000;
212     seg->attrib.raw = 0x9b;
213     seg->limit = 0xffff;
214   }
215   
216   /* Set GPRs */
217   /*
218     EDX == 0xfxx
219     EAX, EBX, ECX, ESI, EDI, EBP, ESP == 0x0
220   */
221
222   guest_state->gdtr.base = 0;
223   guest_state->gdtr.limit = 0xffff;
224   guest_state->gdtr.attrib.raw = 0x0;
225
226   guest_state->idtr.base = 0;
227   guest_state->idtr.limit = 0xffff;
228   guest_state->idtr.attrib.raw = 0x0;
229
230   guest_state->ldtr.base = 0;
231   guest_state->ldtr.limit = 0xffff;
232   guest_state->ldtr.attrib.raw = 0x82;
233
234   guest_state->tr.base = 0;
235   guest_state->tr.limit = 0xffff;
236   guest_state->tr.attrib.raw = 0x83;
237
238
239
240
241   if (vm_info.io_map.num_ports > 0) {
242     vmm_io_hook_t * iter;
243     addr_t io_port_bitmap;
244     
245     io_port_bitmap = (addr_t)os_hooks->allocate_pages(3);
246     memset((uchar_t*)io_port_bitmap, 0, PAGE_SIZE * 3);
247     
248     ctrl_area->IOPM_BASE_PA = io_port_bitmap;
249
250     //PrintDebug("Setting up IO Map at 0x%x\n", io_port_bitmap);
251
252     FOREACH_IO_HOOK(vm_info.io_map, iter) {
253       ushort_t port = iter->port;
254       uchar_t * bitmap = (uchar_t *)io_port_bitmap;
255
256       bitmap += (port / 8);
257       PrintDebug("Setting Bit in block %x\n", bitmap);
258       *bitmap |= 1 << (port % 8);
259     }
260
261     memset((uchar_t*)io_port_bitmap, 0xff, PAGE_SIZE * 2);
262     //PrintDebugMemDump((uchar_t*)io_port_bitmap, PAGE_SIZE *2);
263
264     ctrl_area->instrs.instrs.IOIO_PROT = 1;
265   }
266
267   ctrl_area->instrs.instrs.INTR = 1;
268
269   // also determine if CPU supports nested paging
270   if (vm_info.page_tables) {
271     //   if (0) {
272     // Flush the TLB on entries/exits
273     //ctrl_area->TLB_CONTROL = 1;
274
275     // Enable Nested Paging
276     //ctrl_area->NP_ENABLE = 1;
277
278     //PrintDebug("NP_Enable at 0x%x\n", &(ctrl_area->NP_ENABLE));
279
280         // Set the Nested Page Table pointer
281     //    ctrl_area->N_CR3 = ((addr_t)vm_info.page_tables);
282     ctrl_area->N_CR3 = 0;
283     guest_state->cr3 = (addr_t)(vm_info.page_tables);
284
285     //   ctrl_area->N_CR3 = Get_CR3();
286     // guest_state->cr3 |= (Get_CR3() & 0xfffff000);
287
288     guest_state->g_pat = 0x7040600070406ULL;
289
290     //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));
291   guest_state->cr0 |= 0x80000000;
292   }
293 }
294
295
296 void Init_VMCB(vmcb_t * vmcb, guest_info_t vm_info) {
297   vmcb_ctrl_t * ctrl_area = GET_VMCB_CTRL_AREA(vmcb);
298   vmcb_saved_state_t * guest_state = GET_VMCB_SAVE_STATE_AREA(vmcb);
299   uint_t i;
300
301
302   guest_state->rsp = vm_info.rsp;
303   guest_state->rip = vm_info.rip;
304
305
306
307   ctrl_area->cr_writes.crs.cr0 = 1;
308
309   guest_state->efer |= EFER_MSR_svm_enable;
310   guest_state->rflags = 0x00000002; // The reserved bit is always 1
311   ctrl_area->svm_instrs.instrs.VMRUN = 1;
312   // guest_state->cr0 = 0x00000001;    // PE 
313   ctrl_area->guest_ASID = 1;
314
315
316   ctrl_area->exceptions.ex_names.de = 1;
317   ctrl_area->exceptions.ex_names.df = 1;
318   ctrl_area->exceptions.ex_names.pf = 1;
319   ctrl_area->exceptions.ex_names.ts = 1;
320   ctrl_area->exceptions.ex_names.ss = 1;
321   ctrl_area->exceptions.ex_names.ac = 1;
322   ctrl_area->exceptions.ex_names.mc = 1;
323   ctrl_area->exceptions.ex_names.gp = 1;
324   ctrl_area->exceptions.ex_names.ud = 1;
325   ctrl_area->exceptions.ex_names.np = 1;
326   ctrl_area->exceptions.ex_names.of = 1;
327   ctrl_area->exceptions.ex_names.nmi = 1;
328
329   guest_state->cs.selector = 0x0000;
330   guest_state->cs.limit=~0u;
331   guest_state->cs.base = guest_state->cs.selector<<4;
332   guest_state->cs.attrib.raw = 0xf3;
333
334   
335   struct vmcb_selector *segregs [] = {&(guest_state->ss), &(guest_state->ds), &(guest_state->es), &(guest_state->fs), &(guest_state->gs), NULL};
336   for ( i = 0; segregs[i] != NULL; i++) {
337     struct vmcb_selector * seg = segregs[i];
338     
339     seg->selector = 0x0000;
340     seg->base = seg->selector << 4;
341     seg->attrib.raw = 0xf3;
342     seg->limit = ~0u;
343   }
344   
345   if (vm_info.io_map.num_ports > 0) {
346     vmm_io_hook_t * iter;
347     addr_t io_port_bitmap;
348     
349     io_port_bitmap = (addr_t)os_hooks->allocate_pages(3);
350     memset((uchar_t*)io_port_bitmap, 0, PAGE_SIZE * 3);
351     
352     ctrl_area->IOPM_BASE_PA = io_port_bitmap;
353
354     //PrintDebug("Setting up IO Map at 0x%x\n", io_port_bitmap);
355
356     FOREACH_IO_HOOK(vm_info.io_map, iter) {
357       ushort_t port = iter->port;
358       uchar_t * bitmap = (uchar_t *)io_port_bitmap;
359
360       bitmap += (port / 8);
361       PrintDebug("Setting Bit in block %x\n", bitmap);
362       *bitmap |= 1 << (port % 8);
363     }
364
365
366     //PrintDebugMemDump((uchar_t*)io_port_bitmap, PAGE_SIZE *2);
367
368     ctrl_area->instrs.instrs.IOIO_PROT = 1;
369   }
370
371   ctrl_area->instrs.instrs.INTR = 1;
372
373   // also determine if CPU supports nested paging
374   if (vm_info.page_tables) {
375     //   if (0) {
376     // Flush the TLB on entries/exits
377     //ctrl_area->TLB_CONTROL = 1;
378
379     // Enable Nested Paging
380     //ctrl_area->NP_ENABLE = 1;
381
382     //PrintDebug("NP_Enable at 0x%x\n", &(ctrl_area->NP_ENABLE));
383
384         // Set the Nested Page Table pointer
385     //    ctrl_area->N_CR3 = ((addr_t)vm_info.page_tables);
386     ctrl_area->N_CR3 = 0;
387     guest_state->cr3 = (addr_t)(vm_info.page_tables);
388
389     //   ctrl_area->N_CR3 = Get_CR3();
390     // guest_state->cr3 |= (Get_CR3() & 0xfffff000);
391
392     guest_state->g_pat = 0x7040600070406ULL;
393
394     //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));
395   guest_state->cr0 |= 0x80000000;
396   }
397
398
399
400 }
401
402 void Init_VMCB_pe(vmcb_t *vmcb, guest_info_t vm_info) {
403   vmcb_ctrl_t * ctrl_area = GET_VMCB_CTRL_AREA(vmcb);
404   vmcb_saved_state_t * guest_state = GET_VMCB_SAVE_STATE_AREA(vmcb);
405   uint_t i = 0;
406
407
408   guest_state->rsp = vm_info.rsp;
409   guest_state->rip = vm_info.rip;
410
411
412   /* I pretty much just gutted this from TVMM */
413   /* Note: That means its probably wrong */
414
415   // set the segment registers to mirror ours
416   guest_state->cs.selector = 1<<3;
417   guest_state->cs.attrib.fields.type = 0xa; // Code segment+read
418   guest_state->cs.attrib.fields.S = 1;
419   guest_state->cs.attrib.fields.P = 1;
420   guest_state->cs.attrib.fields.db = 1;
421   guest_state->cs.attrib.fields.G = 1;
422   guest_state->cs.limit = 0xfffff;
423   guest_state->cs.base = 0;
424   
425   struct vmcb_selector *segregs [] = {&(guest_state->ss), &(guest_state->ds), &(guest_state->es), &(guest_state->fs), &(guest_state->gs), NULL};
426   for ( i = 0; segregs[i] != NULL; i++) {
427     struct vmcb_selector * seg = segregs[i];
428     
429     seg->selector = 2<<3;
430     seg->attrib.fields.type = 0x2; // Data Segment+read/write
431     seg->attrib.fields.S = 1;
432     seg->attrib.fields.P = 1;
433     seg->attrib.fields.db = 1;
434     seg->attrib.fields.G = 1;
435     seg->limit = 0xfffff;
436     seg->base = 0;
437   }
438
439
440   {
441     /* JRL THIS HAS TO GO */
442     
443     guest_state->tr.selector = GetTR_Selector();
444     guest_state->tr.attrib.fields.type = 0x9; 
445     guest_state->tr.attrib.fields.P = 1;
446     guest_state->tr.limit = GetTR_Limit();
447     guest_state->tr.base = GetTR_Base();// - 0x2000;
448     /* ** */
449   }
450
451
452   /* ** */
453
454
455   guest_state->efer |= EFER_MSR_svm_enable;
456   guest_state->rflags = 0x00000002; // The reserved bit is always 1
457   ctrl_area->svm_instrs.instrs.VMRUN = 1;
458   guest_state->cr0 = 0x00000001;    // PE 
459   ctrl_area->guest_ASID = 1;
460
461
462   //  guest_state->cpl = 0;
463
464
465
466   // Setup exits
467
468   ctrl_area->cr_writes.crs.cr4 = 1;
469   
470   ctrl_area->exceptions.ex_names.de = 1;
471   ctrl_area->exceptions.ex_names.df = 1;
472   ctrl_area->exceptions.ex_names.pf = 1;
473   ctrl_area->exceptions.ex_names.ts = 1;
474   ctrl_area->exceptions.ex_names.ss = 1;
475   ctrl_area->exceptions.ex_names.ac = 1;
476   ctrl_area->exceptions.ex_names.mc = 1;
477   ctrl_area->exceptions.ex_names.gp = 1;
478   ctrl_area->exceptions.ex_names.ud = 1;
479   ctrl_area->exceptions.ex_names.np = 1;
480   ctrl_area->exceptions.ex_names.of = 1;
481   ctrl_area->exceptions.ex_names.nmi = 1;
482
483   
484
485   ctrl_area->instrs.instrs.IOIO_PROT = 1;
486   ctrl_area->IOPM_BASE_PA = (uint_t)os_hooks->allocate_pages(3);
487   
488   {
489     reg_ex_t tmp_reg;
490     tmp_reg.r_reg = ctrl_area->IOPM_BASE_PA;
491     memset((void*)(tmp_reg.e_reg.low), 0xffffffff, PAGE_SIZE * 2);
492   }
493
494   ctrl_area->instrs.instrs.INTR = 1;
495
496   
497   {
498     char gdt_buf[6];
499     char idt_buf[6];
500
501     memset(gdt_buf, 0, 6);
502     memset(idt_buf, 0, 6);
503
504
505     uint_t gdt_base, idt_base;
506     ushort_t gdt_limit, idt_limit;
507     
508     GetGDTR(gdt_buf);
509     gdt_base = *(ulong_t*)((uchar_t*)gdt_buf + 2) & 0xffffffff;
510     gdt_limit = *(ushort_t*)(gdt_buf) & 0xffff;
511     PrintDebug("GDT: base: %x, limit: %x\n", gdt_base, gdt_limit);
512
513     GetIDTR(idt_buf);
514     idt_base = *(ulong_t*)(idt_buf + 2) & 0xffffffff;
515     idt_limit = *(ushort_t*)(idt_buf) & 0xffff;
516     PrintDebug("IDT: base: %x, limit: %x\n",idt_base, idt_limit);
517
518
519     // gdt_base -= 0x2000;
520     //idt_base -= 0x2000;
521
522     guest_state->gdtr.base = gdt_base;
523     guest_state->gdtr.limit = gdt_limit;
524     guest_state->idtr.base = idt_base;
525     guest_state->idtr.limit = idt_limit;
526
527
528   }
529   
530
531   // also determine if CPU supports nested paging
532   if (vm_info.page_tables) {
533     //   if (0) {
534     // Flush the TLB on entries/exits
535     ctrl_area->TLB_CONTROL = 1;
536
537     // Enable Nested Paging
538     ctrl_area->NP_ENABLE = 1;
539
540     PrintDebug("NP_Enable at 0x%x\n", &(ctrl_area->NP_ENABLE));
541
542         // Set the Nested Page Table pointer
543     ctrl_area->N_CR3 |= ((addr_t)vm_info.page_tables & 0xfffff000);
544
545
546     //   ctrl_area->N_CR3 = Get_CR3();
547     // guest_state->cr3 |= (Get_CR3() & 0xfffff000);
548
549     guest_state->g_pat = 0x7040600070406ULL;
550
551     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));
552     PrintDebug("Set Guest CR3: lo: 0x%x  hi: 0x%x\n", (uint_t)*(&(guest_state->cr3)), (uint_t)*((unsigned char *)&(guest_state->cr3) + 4));
553     // Enable Paging
554     //    guest_state->cr0 |= 0x80000000;
555   }
556
557
558 }
559
560