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.


*** empty log message ***
[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
8
9 extern struct vmm_os_hooks * os_hooks;
10
11 extern uint_t cpuid_ecx(uint_t op);
12 extern uint_t cpuid_edx(uint_t op);
13 extern void Get_MSR(uint_t MSR, uint_t * high_byte, uint_t * low_byte); 
14 extern void Set_MSR(uint_t MSR, uint_t high_byte, uint_t low_byte);
15 extern uint_t launch_svm(vmcb_t * vmcb_addr);
16 extern uint_t Get_CR3();
17
18 /* Checks machine SVM capability */
19 /* Implemented from: AMD Arch Manual 3, sect 15.4 */ 
20 int is_svm_capable() {
21   uint_t ret =  cpuid_ecx(CPUID_FEATURE_IDS);
22   uint_t vm_cr_low = 0, vm_cr_high = 0;
23
24
25   if ((ret & CPUID_FEATURE_IDS_ecx_svm_avail) == 0) {
26     PrintDebug("SVM Not Available\n");
27     return 0;
28   } 
29
30   Get_MSR(SVM_VM_CR_MSR, &vm_cr_high, &vm_cr_low);
31
32   if ((vm_cr_low & SVM_VM_CR_MSR_svmdis) == 0) {
33     return 1;
34   }
35
36   ret = cpuid_edx(CPUID_SVM_REV_AND_FEATURE_IDS);
37   
38
39   if ((ret & CPUID_SVM_REV_AND_FEATURE_IDS_edx_np) == 0) {
40     PrintDebug("Nested Paging not supported\n");
41   }
42
43   if ((ret & CPUID_SVM_REV_AND_FEATURE_IDS_edx_svml) == 0) {
44     PrintDebug("SVM BIOS Disabled, not unlockable\n");
45   } else {
46     PrintDebug("SVM is locked with a key\n");
47   }
48
49   return 0;
50 }
51
52
53
54 void Init_SVM(struct vmm_ctrl_ops * vmm_ops) {
55   reg_ex_t msr;
56   void * host_state;
57
58
59   // Enable SVM on the CPU
60   Get_MSR(EFER_MSR, &(msr.e_reg.high), &(msr.e_reg.low));
61   msr.e_reg.low |= EFER_MSR_svm_enable;
62   Set_MSR(EFER_MSR, 0, msr.e_reg.low);
63   
64   PrintDebug("SVM Enabled\n");
65
66
67   // Setup the host state save area
68   host_state = os_hooks->allocate_pages(1);
69   
70   msr.e_reg.high = 0;
71   msr.e_reg.low = (uint_t)host_state;
72
73
74   PrintDebug("Host State being saved at %x\n", (uint_t)host_state);
75   Set_MSR(SVM_VM_HSAVE_PA_MSR, msr.e_reg.high, msr.e_reg.low);
76
77
78
79   // Setup the SVM specific vmm operations
80   vmm_ops->init_guest = &init_svm_guest;
81   vmm_ops->start_guest = &start_svm_guest;
82
83
84   return;
85 }
86
87
88 int init_svm_guest(struct guest_info *info) {
89  
90   PrintDebug("Allocating VMCB\n");
91   info->vmm_data = (void*)Allocate_VMCB();
92
93
94   PrintDebug("Generating Guest nested page tables\n");
95   print_mem_list(&(info->mem_list));
96   print_mem_layout(&(info->mem_layout));
97   info->page_tables = generate_guest_page_tables(&(info->mem_layout), &(info->mem_list));
98   //PrintDebugPageTables(info->page_tables);
99
100   
101
102   PrintDebug("Initializing VMCB (addr=%x)\n", info->vmm_data);
103   Init_VMCB((vmcb_t*)(info->vmm_data), *info);
104
105
106   
107
108   return 0;
109 }
110
111
112 // can we start a kernel thread here...
113 int start_svm_guest(struct guest_info *info) {
114   vmcb_ctrl_t * guest_ctrl = 0;
115
116   ulong_t exit_code = 0;
117
118   PrintDebug("Launching SVM VM (vmcb=%x)\n", info->vmm_data);
119
120   launch_svm((vmcb_t*)(info->vmm_data));
121
122   guest_ctrl = GET_VMCB_CTRL_AREA((vmcb_t*)(info->vmm_data));
123
124
125   PrintDebug("SVM Returned: (Exit Code=%x) (VMCB=%x)\n",&(guest_ctrl->exit_code), info->vmm_data); 
126
127
128   exit_code = guest_ctrl->exit_code;
129
130   PrintDebug("SVM Returned: Exit Code: %x\n",exit_code); 
131
132   return 0;
133 }
134
135
136
137 /** 
138  *  We handle the svm exits here
139  *  This function should probably be moved to another file to keep things managable....
140  */
141 int handle_svm_exit(struct VMM_GPRs guest_gprs) {
142
143   return 0;
144 }
145
146
147 vmcb_t * Allocate_VMCB() {
148   vmcb_t * vmcb_page = (vmcb_t*)os_hooks->allocate_pages(1);
149
150
151   memset(vmcb_page, 0, 4096);
152
153   return vmcb_page;
154 }
155
156
157
158 void Init_VMCB(vmcb_t *vmcb, guest_info_t vm_info) {
159   vmcb_ctrl_t * ctrl_area = GET_VMCB_CTRL_AREA(vmcb);
160   vmcb_saved_state_t * guest_state = GET_VMCB_SAVE_STATE_AREA(vmcb);
161   uint_t i = 0;
162
163
164   guest_state->rsp = vm_info.rsp;
165   guest_state->rip = vm_info.rip;
166
167
168   /* I pretty much just gutted this from TVMM */
169   /* Note: That means its probably wrong */
170
171   // set the segment registers to mirror ours
172   guest_state->cs.selector = 0;
173   guest_state->cs.attrib.fields.type = 0xa; // Code segment+read
174   guest_state->cs.attrib.fields.S = 1;
175   guest_state->cs.attrib.fields.P = 1;
176   guest_state->cs.attrib.fields.db = 1;
177   guest_state->cs.limit = 0xffffffff;
178   guest_state->cs.base = 0;
179   
180   struct vmcb_selector *segregs [] = {&(guest_state->ss), &(guest_state->ds), &(guest_state->es), &(guest_state->fs), &(guest_state->gs), NULL};
181   for ( i = 0; segregs[i] != NULL; i++) {
182     struct vmcb_selector * seg = segregs[i];
183     
184     seg->selector = 0;
185     seg->attrib.fields.type = 0x2; // Data Segment+read/write
186     seg->attrib.fields.S = 1;
187     seg->attrib.fields.P = 1;
188     seg->attrib.fields.db = 1;
189     seg->limit = 0xffffffff;
190     seg->base = 0;
191   }
192
193   /* ** */
194
195
196   guest_state->efer |= EFER_MSR_svm_enable;
197   guest_state->rflags = 0x00000002; // The reserved bit is always 1
198   ctrl_area->svm_instrs.instrs.VMRUN = 1;
199   guest_state->cr0 = 0x00000001;    // PE 
200   ctrl_area->guest_ASID = 1;
201
202
203   // Setup exits
204
205   
206   ctrl_area->exceptions.ex_names.de = 1;
207   ctrl_area->exceptions.ex_names.df = 1;
208   ctrl_area->exceptions.ex_names.pf = 1;
209   ctrl_area->exceptions.ex_names.ts = 1;
210   ctrl_area->exceptions.ex_names.ss = 1;
211   ctrl_area->exceptions.ex_names.ac = 1;
212   ctrl_area->exceptions.ex_names.mc = 1;
213   ctrl_area->exceptions.ex_names.gp = 1;
214   ctrl_area->exceptions.ex_names.ud = 1;
215   ctrl_area->exceptions.ex_names.np = 1;
216   ctrl_area->exceptions.ex_names.of = 1;
217   
218   // also determine if CPU supports nested paging
219   if (vm_info.page_tables) {
220     // Flush the TLB on entries/exits
221     ctrl_area->TLB_CONTROL = 1;
222
223     // Enable Nested Paging
224     ctrl_area->NP_ENABLE = 1;
225
226         // Set the Nested Page Table pointer
227     ctrl_area->N_CR3 |= ((addr_t)vm_info.page_tables & 0xfffff000);
228     
229     guest_state->cr3 = Get_CR3();
230
231     PrintDebug("Set Nested CR3: lo: 0x%x  hi: 0x%x\n", (uint_t)*(&(ctrl_area->N_CR3)), (uint_t)*(&(ctrl_area->N_CR3) + 4));
232     PrintDebug("Set Guest CR3: lo: 0x%x  hi: 0x%x\n", (uint_t)*(&(guest_state->cr3)), (uint_t)*(&(guest_state->cr3) + 4));
233     // Enable Paging
234     guest_state->cr0 |= 0x80000000;
235   }
236
237
238 }
239
240