github.com/jlmucb/cloudproxy@v0.0.0-20170830161738-b5aa0b619bc4/cpvmm/vmm/memory/ept/ept.c (about) 1 /* 2 * Copyright (c) 2013 Intel Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * Unless required by applicable law or agreed to in writing, software 9 * distributed under the License is distributed on an "AS IS" BASIS, 10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 * See the License for the specific language governing permissions and 12 * limitations under the License. 13 */ 14 15 #include "file_codes.h" 16 #define VMM_DEADLOOP() VMM_DEADLOOP_LOG(EPT_C) 17 #define VMM_ASSERT(__condition) VMM_ASSERT_LOG(EPT_C, __condition) 18 #include "vmm_callback.h" 19 #include "vmcs_init.h" 20 #include "guest_cpu.h" 21 #include "event_mgr.h" 22 #include "vmm_events_data.h" 23 #include "vmcs_api.h" 24 #include "guest.h" 25 #include "ept.h" 26 #include "policy_manager.h" 27 #include "memory_allocator.h" 28 #include "memory_address_mapper_api.h" 29 #include "gpm_api.h" 30 #include "hw_utils.h" 31 #include "mtrrs_abstraction.h" 32 #include "libc.h" 33 #include "host_memory_manager_api.h" 34 #include "ept_hw_layer.h" 35 #include "ipc.h" 36 #include "guest_cpu_vmenter_event.h" 37 #include "lock.h" 38 #include "scheduler.h" 39 #include "page_walker.h" 40 #include "guest_cpu_internal.h" 41 #include "unrestricted_guest.h" 42 #include "fvs.h" 43 #include "ve.h" 44 #ifdef JLMDEBUG 45 #include "jlmdebug.h" 46 #endif 47 48 EPT_STATE ept; 49 HPA redirect_physical_addr = 0; 50 51 #pragma warning( disable : 4214 ) // enables UINT64 bitfield 52 #pragma warning (disable : 4100) // Supress warnings about unreferenced formal parameter 53 54 // macro #define's 55 #define PDPTR_NXE_DISABLED_RESERVED_BITS_MASK (UINT64) 0xffffff00000001e6 56 #define PDPTR_NXE_ENABLED_RESERVED_BITS_MASK (UINT64) 0x7fffff00000001e6 57 #define PRESENT_BIT (UINT64) 0x1 58 59 // static functions 60 static BOOLEAN ept_guest_cpu_initialize(GUEST_CPU_HANDLE gcpu); 61 BOOLEAN ept_page_walk(UINT64 first_table, UINT64 addr, UINT32 gaw); 62 void ept_set_remote_eptp(CPU_ID from, void* arg); 63 64 65 #ifdef INCLUDE_UNUSED_CODE 66 static 67 BOOLEAN ept_check_pdpt_reserved_bits(UINT64 pdptr, UINT64 efer) 68 { 69 if((efer & EFER_NXE) == 0) { 70 return (pdptr & PDPTR_NXE_DISABLED_RESERVED_BITS_MASK) == 0; 71 } 72 return (pdptr & PDPTR_NXE_ENABLED_RESERVED_BITS_MASK) == 0; 73 } 74 #endif 75 76 void ept_set_pdtprs(GUEST_CPU_HANDLE gcpu, UINT64 cr4_value) 77 { 78 UINT64 pdpt[4]; 79 BOOLEAN status = TRUE; 80 BOOLEAN pdptr_required = FALSE; 81 82 if (cr4_value & CR4_PAE) { // PAE mode 83 UINT64 efer = gcpu_get_msr_reg(gcpu, IA32_VMM_MSR_EFER); 84 if (0 == (efer & EFER_LME)) { // 32-bit mode 85 status = gcpu_get_32_bit_pdpt(gcpu, pdpt) 86 && pw_is_pdpt_in_32_bit_pae_mode_valid(gcpu, pdpt); 87 if (TRUE == status) { 88 pdptr_required = TRUE; 89 ept_hw_set_pdtprs(gcpu, pdpt); 90 } 91 } 92 } 93 if (FALSE == pdptr_required) { 94 vmm_zeromem(pdpt, sizeof(pdpt)); 95 ept_hw_set_pdtprs(gcpu, pdpt); 96 } 97 } 98 99 void ept_acquire_lock(void) 100 { 101 #ifdef JLMDEBUG1 102 bprint("(ept_acquire_lock %d)", ept.lock_count); 103 #endif 104 if (ept.lock.owner_cpu_id == hw_cpu_id()) { 105 ept.lock_count++; 106 return; 107 } 108 interruptible_lock_acquire(&ept.lock); 109 ept.lock_count = 1; 110 } 111 112 void ept_release_lock(void) 113 { 114 ept.lock_count--; 115 if (ept.lock_count == 0) { 116 lock_release(&ept.lock); 117 } 118 } 119 120 BOOLEAN ept_is_cpu_in_non_paged_mode(GUEST_ID guest_id) 121 { 122 EPT_GUEST_STATE *ept_guest = NULL; 123 EPT_GUEST_CPU_STATE *ept_guest_cpu = NULL; 124 UINT32 i = 0; 125 GUEST_CPU_HANDLE gcpu = scheduler_get_current_gcpu_for_guest(guest_id); 126 127 //for UG system, flat page table will never be used, so, this function should always return FALSE. 128 if(is_unrestricted_guest_enabled(gcpu)) 129 return FALSE; 130 ept_guest = ept_find_guest_state(guest_id); 131 VMM_ASSERT(ept_guest); 132 for (i = 0; i < ept.num_of_cpus; i++) { 133 ept_guest_cpu = ept_guest->gcpu_state[i]; 134 VMM_ASSERT(ept_guest_cpu); 135 if (ept_guest_cpu->is_initialized && (ept_guest_cpu->cr0 & CR0_PG) == 0) { 136 // cannot change perms - another gcpu not paged and uses flat page tables 137 return TRUE; 138 } 139 } 140 return FALSE; 141 } 142 143 144 #ifdef INCLUDE_UNUSED_CODE 145 void dbg_print_ept_violation(GUEST_CPU_HANDLE gcpu, EPTP eptp, EVENT_GCPU_EPT_VIOLATION_DATA *data) 146 { 147 EPT_PRINTERROR("\r\n****EPT violation:****\n"); 148 EPT_PRINTERROR("R=%d W=%d X=%d EptR=%d EptW=%d EptX=%d\n", 149 data->qualification.EptViolation.R, data->qualification.EptViolation.W, data->qualification.EptViolation.X, 150 data->qualification.EptViolation.EptR, data->qualification.EptViolation.EptW, data->qualification.EptViolation.EptX); 151 EPT_PRINTERROR("GawViolation=%d GlaValidity=%d NMIunblocking=%d\n", 152 data->qualification.EptViolation.GawViolation, data->qualification.EptViolation.GlaValidity,data->qualification.EptViolation.NMIunblocking); 153 EPT_PRINTERROR("GPA: %p\n", data->guest_physical_address); 154 if(data->qualification.EptViolation.GlaValidity) 155 { 156 EPT_PRINTERROR("GVA: %p\n", data->guest_linear_address); 157 } 158 EPT_PRINTERROR("EPTP.ETMT: 0x%X EPTP.GAW: 0x%X EPTP.ASR: 0x%X\n", eptp.Bits.ETMT, eptp.Bits.GAW, eptp.Uint64 & ~PAGE_4KB_MASK); 159 EPT_PRINTERROR("Is native %p\r\n", gcpu_is_native_execution(gcpu)); 160 ept_page_walk((UINT64) eptp.Uint64 & ~PAGE_4KB_MASK, data->guest_physical_address, ept_hw_get_guest_address_width_from_encoding((UINT32)eptp.Bits.GAW)); 161 } 162 #endif 163 164 // EPT vmexits 165 /* 166 * Function name: ept_violation_vmexit 167 * Parameters: Function does not validate gcpu. Assumes valid. 168 */ 169 BOOLEAN ept_violation_vmexit(GUEST_CPU_HANDLE gcpu, void *pv) 170 { 171 REPORT_EPT_VIOLATION_DATA violation_data; 172 EVENT_GCPU_EPT_VIOLATION_DATA *data = (EVENT_GCPU_EPT_VIOLATION_DATA *) pv; 173 const VIRTUAL_CPU_ID *vcpu_id=NULL; 174 IA32_VMX_EXIT_QUALIFICATION ept_violation_qualification; 175 176 vcpu_id= guest_vcpu(gcpu); 177 VMM_ASSERT(vcpu_id); 178 // Report EPT violation to the VIEW module 179 violation_data.qualification = data->qualification.Uint64; 180 violation_data.guest_linear_address = data->guest_linear_address; 181 violation_data.guest_physical_address = data->guest_physical_address; 182 183 ept_violation_qualification.Uint64 = violation_data.qualification; 184 if( ept_violation_qualification.EptViolation.NMIunblocking ) { 185 VMCS_OBJECT *vmcs = gcpu_get_vmcs(gcpu); 186 IA32_VMX_VMCS_VM_EXIT_INFO_IDT_VECTORING idt_vectoring_info; 187 188 idt_vectoring_info.Uint32 = (UINT32)vmcs_read(vmcs,VMCS_EXIT_INFO_IDT_VECTORING); 189 190 if(!idt_vectoring_info.Bits.Valid) { 191 IA32_VMX_VMCS_GUEST_INTERRUPTIBILITY guest_interruptibility; 192 193 guest_interruptibility.Uint32 = (UINT32) vmcs_read(vmcs, VMCS_GUEST_INTERRUPTIBILITY); 194 guest_interruptibility.Bits.BlockNmi = 1; 195 vmcs_write(vmcs,VMCS_GUEST_INTERRUPTIBILITY,(UINT64)guest_interruptibility.Uint32); 196 } 197 } 198 199 if (!report_uvmm_event(UVMM_EVENT_EPT_VIOLATION, (VMM_IDENTIFICATION_DATA)gcpu, (const GUEST_VCPU*)vcpu_id, (void *)&violation_data)) { 200 VMM_LOG(mask_anonymous, level_trace, "report_ept_violation failed\n"); 201 } 202 203 data->processed = TRUE; 204 return TRUE; 205 } 206 207 #pragma warning (disable:4189) 208 #pragma warning (disable:4101) 209 210 BOOLEAN ept_misconfiguration_vmexit(GUEST_CPU_HANDLE gcpu UNUSED, void *pv) 211 { 212 EPTP eptp; 213 EVENT_GCPU_EPT_MISCONFIGURATION_DATA *data = (EVENT_GCPU_EPT_MISCONFIGURATION_DATA *) pv; 214 215 EPT_PRINTERROR("\r\n****EPT Misconfiguration:****\n"); 216 EPT_PRINTERROR("GPA=%p\n", data->guest_physical_address); 217 218 eptp.Uint64 = ept_get_eptp(gcpu); 219 VMM_LOG(mask_anonymous, level_trace,"EPTP.ETMT: 0x%X EPTP.GAW: 0x%X EPTP.ASR: 0x%X\n", eptp.Bits.ETMT, eptp.Bits.GAW, eptp.Uint64 & ~PAGE_4KB_MASK); 220 VMM_LOG(mask_anonymous, level_trace,"Is native %p\r\n", gcpu_is_native_execution(gcpu)); 221 ept_page_walk((UINT64) eptp.Uint64 & ~PAGE_4KB_MASK, data->guest_physical_address, ept_hw_get_guest_address_width_from_encoding((UINT32)eptp.Bits.GAW)); 222 VMM_DEADLOOP(); 223 data->processed = TRUE; 224 return TRUE; 225 } 226 227 MAM_EPT_SUPER_PAGE_SUPPORT ept_get_mam_super_page_support(void) 228 { 229 const VMCS_HW_CONSTRAINTS *hw_constraints = vmcs_hw_get_vmx_constraints(); 230 IA32_VMX_EPT_VPID_CAP ept_cap = hw_constraints->ept_vpid_capabilities; 231 MAM_EPT_SUPER_PAGE_SUPPORT sp_support = MAM_EPT_NO_SUPER_PAGE_SUPPORT; 232 233 // Currently we support 2MB pages in implementation 234 if(ept_cap.Bits.SP_21_bit) { 235 sp_support |= MAM_EPT_SUPPORT_2MB_PAGE; 236 } 237 #if 0 // Support for different memory page sizes 238 if(ept_cap.Bits.SP_30_bit) { 239 sp_support |= MAM_EPT_SUPPORT_1GB_PAGE; 240 } 241 if(ept_cap.Bits.SP_39_bit) 242 { 243 sp_support |= MAM_EPT_SUPPORT_512_GB_PAGE; 244 } 245 #endif 246 return sp_support; 247 } 248 249 void ept_get_current_ept(GUEST_CPU_HANDLE gcpu, UINT64 *ept_root_table_hpa, UINT32 *ept_gaw) 250 { 251 const VIRTUAL_CPU_ID* vcpu_id = NULL; 252 EPT_GUEST_STATE *ept_guest = NULL; 253 EPT_GUEST_CPU_STATE *ept_guest_cpu = NULL; 254 VMM_ASSERT(gcpu); 255 vcpu_id = guest_vcpu(gcpu); 256 //paranoid check. If assertion fails, possible memory corruption. 257 VMM_ASSERT(vcpu_id); 258 ept_guest = ept_find_guest_state(vcpu_id->guest_id); 259 VMM_ASSERT(ept_guest); 260 ept_guest_cpu = ept_guest->gcpu_state[vcpu_id->guest_cpu_id]; 261 VMM_ASSERT(ept_guest_cpu); 262 *ept_root_table_hpa = ept_guest_cpu->active_ept_root_table_hpa; 263 *ept_gaw = ept_guest_cpu->active_ept_gaw; 264 } 265 266 void ept_set_current_ept(GUEST_CPU_HANDLE gcpu, UINT64 ept_root_table_hpa, UINT32 ept_gaw) 267 { 268 const VIRTUAL_CPU_ID* vcpu_id = NULL; 269 EPT_GUEST_STATE *ept_guest = NULL; 270 EPT_GUEST_CPU_STATE *ept_guest_cpu = NULL; 271 272 VMM_ASSERT(gcpu); 273 vcpu_id = guest_vcpu(gcpu); 274 //paranoid check. If assertion fails, possible memory corruption. 275 VMM_ASSERT(vcpu_id); 276 ept_guest = ept_find_guest_state(vcpu_id->guest_id); 277 VMM_ASSERT(ept_guest); 278 ept_guest_cpu = ept_guest->gcpu_state[vcpu_id->guest_cpu_id]; 279 VMM_ASSERT(ept_guest_cpu); 280 ept_guest_cpu->active_ept_root_table_hpa = ept_root_table_hpa; 281 ept_guest_cpu->active_ept_gaw = ept_gaw; 282 } 283 284 void ept_get_default_ept(GUEST_HANDLE guest, UINT64 *ept_root_table_hpa, UINT32 *ept_gaw) 285 { 286 EPT_GUEST_STATE *ept_guest = NULL; 287 288 VMM_ASSERT(guest); 289 290 ept_guest = ept_find_guest_state(guest_get_id(guest)); 291 VMM_ASSERT(ept_guest); 292 293 *ept_root_table_hpa = ept_guest->ept_root_table_hpa; 294 *ept_gaw = ept_guest->gaw; 295 } 296 297 void ept_create_default_ept(GUEST_HANDLE guest, GPM_HANDLE gpm) 298 { 299 EPT_GUEST_STATE *ept_guest = NULL; 300 301 VMM_ASSERT(guest); 302 VMM_ASSERT(gpm); 303 ept_guest = ept_find_guest_state(guest_get_id(guest)); 304 VMM_ASSERT(ept_guest); 305 306 if (ept_guest->address_space != MAM_INVALID_HANDLE) { 307 mam_destroy_mapping(ept_guest->address_space); 308 ept_guest->address_space = MAM_INVALID_HANDLE; 309 } 310 ept_guest->gaw = ept_hw_get_guest_address_width(ept_get_guest_address_width(gpm)); 311 VMM_ASSERT(ept_guest->gaw != (UINT32) -1); 312 ept_guest->address_space = ept_create_guest_address_space(gpm, TRUE); 313 VMM_ASSERT(mam_convert_to_ept(ept_guest->address_space, ept_get_mam_super_page_support(), 314 ept_get_mam_supported_gaw(ept_guest->gaw), ve_is_hw_supported(), 315 &(ept_guest->ept_root_table_hpa))); 316 } 317 318 MAM_EPT_SUPPORTED_GAW ept_get_mam_supported_gaw(UINT32 gaw) 319 { 320 return (MAM_EPT_SUPPORTED_GAW)ept_hw_get_guest_address_width_encoding(gaw); 321 } 322 323 static 324 BOOLEAN ept_begin_gpm_modification_before_cpus_stop( GUEST_CPU_HANDLE gcpu UNUSED, 325 void* pv UNUSED ) 326 { 327 ept_acquire_lock(); 328 return TRUE; 329 } 330 331 static 332 BOOLEAN ept_end_gpm_modification_before_cpus_resume( GUEST_CPU_HANDLE gcpu, void* pv ) 333 { 334 GUEST_HANDLE guest = NULL; 335 EPT_SET_EPTP_CMD set_eptp_cmd; 336 EPT_INVEPT_CMD invept_cmd; 337 IPC_DESTINATION ipc_dest; 338 EVENT_GPM_MODIFICATION_DATA *gpm_modification_data = (EVENT_GPM_MODIFICATION_DATA *) pv; 339 UINT64 default_ept_root_table_hpa; 340 UINT32 default_ept_gaw; 341 VMM_ASSERT(pv); 342 343 guest = guest_handle(gpm_modification_data->guest_id); 344 if (gpm_modification_data->operation == VMM_MEM_OP_UPDATE) 345 { 346 ept_get_default_ept(guest, &default_ept_root_table_hpa, &default_ept_gaw); 347 invept_cmd.host_cpu_id = ANY_CPU_ID; 348 invept_cmd.cmd = INVEPT_CONTEXT_WIDE; 349 invept_cmd.eptp = ept_compute_eptp(guest, default_ept_root_table_hpa, default_ept_gaw); 350 351 ept_invalidate_ept(ANY_CPU_ID, &invept_cmd); 352 353 ipc_dest.addr_shorthand = IPI_DST_ALL_EXCLUDING_SELF; 354 ipc_execute_handler_sync(ipc_dest, ept_invalidate_ept, (void *) &invept_cmd); 355 } else if (gpm_modification_data->operation == VMM_MEM_OP_RECREATE) { 356 // Recreate Default EPT 357 ept_create_default_ept(guest, guest_get_startup_gpm(guest)); 358 ept_get_default_ept(guest, &default_ept_root_table_hpa, &default_ept_gaw); 359 360 // Reset the Default EPT on current CPU 361 ept_set_eptp(gcpu, default_ept_root_table_hpa, default_ept_gaw); 362 363 invept_cmd.host_cpu_id = ANY_CPU_ID; 364 invept_cmd.cmd = INVEPT_CONTEXT_WIDE; 365 invept_cmd.eptp = ept_compute_eptp(guest, default_ept_root_table_hpa, default_ept_gaw); 366 ept_invalidate_ept(ANY_CPU_ID, &invept_cmd); 367 368 set_eptp_cmd.guest_id = gpm_modification_data->guest_id; 369 set_eptp_cmd.ept_root_table_hpa = default_ept_root_table_hpa; 370 set_eptp_cmd.gaw = default_ept_gaw; 371 set_eptp_cmd.invept_cmd = &invept_cmd; 372 373 ipc_dest.addr_shorthand = IPI_DST_ALL_EXCLUDING_SELF; 374 ipc_execute_handler_sync(ipc_dest, ept_set_remote_eptp, (void *) &set_eptp_cmd); 375 } else { // switch 376 VMM_ASSERT(gpm_modification_data->operation == VMM_MEM_OP_SWITCH); 377 //only switch ept if the active view is not the same as switchto handle 378 // if (ept_guest_get_active_view(gcpu) != gpm_modification_data->handle) { 379 // VMM_ASSERT(ept_set_eptp(gcpu, gpm_modification_data->handle)); 380 // } 381 } 382 383 return TRUE; 384 } 385 386 387 static BOOLEAN ept_end_gpm_modification_after_cpus_resume(GUEST_CPU_HANDLE gcpu UNUSED, void* pv UNUSED) 388 { 389 ept_release_lock(); 390 return TRUE; 391 } 392 393 394 static BOOLEAN ept_cr0_update(GUEST_CPU_HANDLE gcpu, void* pv) 395 { 396 UINT64 value = ((EVENT_GCPU_GUEST_CR_WRITE_DATA*) pv)->new_guest_visible_value; 397 BOOLEAN pg; 398 BOOLEAN prev_pg = 0; 399 const VIRTUAL_CPU_ID* vcpu_id = NULL; 400 EPT_GUEST_STATE *ept_guest = NULL; 401 EPT_GUEST_CPU_STATE *ept_guest_cpu = NULL; 402 UINT64 cr4; 403 IA32_EFER_S efer; 404 VM_ENTRY_CONTROLS entry_ctrl_mask; 405 VMCS_OBJECT* vmcs = gcpu_get_vmcs(gcpu); 406 407 #ifdef JLMDEBUG 408 bprint("ept_cr0_update, "); 409 #endif 410 ept_acquire_lock(); 411 vcpu_id = guest_vcpu(gcpu); 412 VMM_ASSERT(vcpu_id); 413 ept_guest = ept_find_guest_state(vcpu_id->guest_id); 414 VMM_ASSERT(ept_guest); 415 ept_guest_cpu = ept_guest->gcpu_state[vcpu_id->guest_cpu_id]; 416 prev_pg = (ept_guest_cpu->cr0 & CR0_PG) != 0; 417 ept_guest_cpu->cr0 = value; 418 pg = (ept_guest_cpu->cr0 & CR0_PG) != 0; 419 if(is_unrestricted_guest_supported()) { 420 /* IA Manual 3B: 27.9.4: IA32_EFER.LMA is always set by the processor 421 * to equal IA32_EFER.LME & CR0.PG 422 * Update LMA and IA32e bits based on LME and PG bit on systems with UG 423 * Set VMCS.GUEST.EFER_MSR.LMA = (GUEST.CR0.PG & GUEST.EFER.LME) 424 * Set VMCS.ENTRY_CONTROL.IA32e = (GUEST.CR0.PG & GUEST.EFER.LME) 425 * 426 * On systems w/o UG, LMA and IA32e are updated when EFER.LME is updated, 427 * since PG is always 1 428 */ 429 efer.Uint64 = gcpu_get_msr_reg(gcpu, IA32_VMM_MSR_EFER); 430 efer.Bits.LMA = (pg & efer.Bits.LME); 431 gcpu_set_msr_reg(gcpu, IA32_VMM_MSR_EFER, efer.Uint64); 432 entry_ctrl_mask.Uint32 = 0; 433 entry_ctrl_mask.Bits.Ia32eModeGuest = 1; 434 vmcs_update(vmcs, VMCS_ENTER_CONTROL_VECTOR, 435 (efer.Bits.LMA) ? UINT64_ALL_ONES : 0, 436 (UINT64) entry_ctrl_mask.Uint32); 437 } 438 if(pg != prev_pg) { 439 /* INVVPID for this guest */ 440 ept_hw_invvpid_single_context(1 + gcpu->vcpu.guest_id); 441 } 442 if((pg) && (pg != prev_pg)) { 443 // Enable EPT on systems w/o UG, when PG is turned on 444 if(!is_unrestricted_guest_supported() && !ept_is_ept_enabled(gcpu)) 445 ept_enable(gcpu); 446 cr4 = gcpu_get_guest_visible_control_reg(gcpu, IA32_CTRL_CR4); 447 ept_set_pdtprs(gcpu, cr4); 448 } 449 // Disable EPT on systems without UG, when PG is turned off 450 if(!pg && !is_unrestricted_guest_supported() && ept_is_ept_enabled(gcpu)) 451 ept_disable(gcpu); 452 ept_release_lock(); 453 #ifdef JLMDEBUG 454 bprint("ept_cr0_update complete\n"); 455 #endif 456 return TRUE; 457 } 458 459 static BOOLEAN ept_cr3_update( GUEST_CPU_HANDLE gcpu, void* pv UNUSED ) 460 { 461 const VIRTUAL_CPU_ID* vcpu_id = NULL; 462 EPT_GUEST_STATE *ept_guest = NULL; 463 EPT_GUEST_CPU_STATE *ept_guest_cpu = NULL; 464 465 #ifdef JLMDEBUG 466 bprint("ept_cr3_update\n"); 467 #endif 468 ept_acquire_lock(); 469 vcpu_id = guest_vcpu( gcpu ); 470 VMM_ASSERT(vcpu_id); 471 ept_guest = ept_find_guest_state(vcpu_id->guest_id); 472 VMM_ASSERT(ept_guest); 473 ept_guest_cpu = ept_guest->gcpu_state[vcpu_id->guest_cpu_id]; 474 if ((ept_guest_cpu->cr0 & CR0_PG) && // if paging is enabled 475 (ept_guest_cpu->cr4 & CR4_PAE)) { // and PAE mode is active 476 ept_set_pdtprs(gcpu, ept_guest_cpu->cr4); 477 } 478 // Flush TLB 479 ept_hw_invvpid_single_context(1 + gcpu->vcpu.guest_id); 480 ept_release_lock(); 481 // EPT_LOG("EPT CPU#%d: %s\n", hw_cpu_id(), __FUNCTION__); 482 return TRUE; 483 } 484 485 486 static BOOLEAN ept_cr4_update(GUEST_CPU_HANDLE gcpu, void* pv) 487 { 488 UINT64 new_cr4 = ((EVENT_GCPU_GUEST_CR_WRITE_DATA*) pv)->new_guest_visible_value; 489 BOOLEAN pg; 490 BOOLEAN pae = 0; 491 BOOLEAN prev_pae = 0; 492 const VIRTUAL_CPU_ID* vcpu_id = NULL; 493 EPT_GUEST_STATE *ept_guest = NULL; 494 EPT_GUEST_CPU_STATE *ept_guest_cpu = NULL; 495 UINT64 cr4; 496 497 (void)pg; 498 #ifdef JLMDEBUG 499 bprint("ept_cr4_update, \n"); 500 #endif 501 ept_acquire_lock(); 502 vcpu_id = guest_vcpu(gcpu); 503 VMM_ASSERT(vcpu_id); 504 ept_guest = ept_find_guest_state(vcpu_id->guest_id); 505 VMM_ASSERT(ept_guest); 506 ept_guest_cpu = ept_guest->gcpu_state[vcpu_id->guest_cpu_id]; 507 prev_pae = (ept_guest_cpu->cr4 & CR4_PAE) != 0; 508 ept_guest_cpu->cr4 = new_cr4; 509 pg = (ept_guest_cpu->cr0 & CR0_PG) != 0; 510 pae = (ept_guest_cpu->cr4 & CR4_PAE) != 0; 511 if(ept_is_ept_enabled(gcpu) && pae != prev_pae) { 512 cr4 = ept_guest_cpu->cr4; 513 ept_set_pdtprs(gcpu, cr4); 514 } 515 // Flush TLB 516 ept_hw_invvpid_single_context(1+gcpu->vcpu.guest_id); 517 ept_release_lock(); 518 #ifdef JLMDEBUG 519 bprint("ept_cr4_update complete\n"); 520 #endif 521 return TRUE; 522 } 523 524 static BOOLEAN ept_emulator_enter(GUEST_CPU_HANDLE gcpu, void* pv UNUSED) 525 { 526 const VIRTUAL_CPU_ID* vcpu_id = NULL; 527 EPT_GUEST_CPU_STATE *ept_guest_cpu = NULL; 528 EPT_GUEST_STATE *ept_guest_state = NULL; 529 530 vcpu_id = guest_vcpu( gcpu ); 531 VMM_ASSERT(vcpu_id); 532 ept_guest_state = ept_find_guest_state(vcpu_id->guest_id); 533 VMM_ASSERT(ept_guest_state); 534 ept_guest_cpu = ept_guest_state->gcpu_state[vcpu_id->guest_cpu_id]; 535 536 ept_guest_cpu->cr0 = gcpu_get_guest_visible_control_reg(gcpu, IA32_CTRL_CR0); 537 ept_guest_cpu->cr4 = gcpu_get_guest_visible_control_reg(gcpu, IA32_CTRL_CR4); 538 ept_guest_cpu->ept_enabled_save = FALSE; 539 if(ept_is_ept_enabled(gcpu)) { 540 ept_guest_cpu->ept_enabled_save = TRUE; 541 ept_disable(gcpu); 542 } 543 return TRUE; 544 } 545 546 static BOOLEAN ept_emulator_exit(GUEST_CPU_HANDLE gcpu, void* pv UNUSED) 547 { 548 const VIRTUAL_CPU_ID* vcpu_id = NULL; 549 EPT_GUEST_STATE *ept_guest = NULL; 550 EPT_GUEST_CPU_STATE *ept_guest_cpu = NULL; 551 EVENT_GCPU_GUEST_CR_WRITE_DATA write_data = {0}; 552 UINT64 cr0, cr4; 553 554 #ifdef JLMDEBUG 555 bprint("ept_emulator exit\n"); 556 #endif 557 ept_acquire_lock(); 558 vcpu_id = guest_vcpu(gcpu); 559 VMM_ASSERT(vcpu_id); 560 ept_guest = ept_find_guest_state(vcpu_id->guest_id); 561 VMM_ASSERT(ept_guest); 562 ept_guest_cpu = ept_guest->gcpu_state[vcpu_id->guest_cpu_id]; 563 if(ept_guest_cpu->ept_enabled_save) { 564 ept_enable(gcpu); 565 } 566 cr0 = gcpu_get_guest_visible_control_reg(gcpu, IA32_CTRL_CR0); 567 cr4 = gcpu_get_guest_visible_control_reg(gcpu, IA32_CTRL_CR4); 568 // Do not assume that the CR0 must be changed when emulator exits. 569 // comment out this line to fix the issue "ETP disabled after S3 in ThinkCentre desktop". 570 if(cr0 != ept_guest_cpu->cr0) { 571 write_data.new_guest_visible_value = cr0; 572 ept_cr0_update(gcpu, &write_data); 573 } 574 if(cr4 != ept_guest_cpu->cr4) { 575 write_data.new_guest_visible_value = cr4; 576 ept_cr4_update(gcpu, &write_data); 577 } 578 ept_release_lock(); 579 return TRUE; 580 } 581 582 static void ept_register_events(GUEST_CPU_HANDLE gcpu) 583 { 584 event_gcpu_register(EVENT_GCPU_AFTER_GUEST_CR0_WRITE, gcpu, ept_cr0_update); 585 event_gcpu_register(EVENT_GCPU_AFTER_GUEST_CR3_WRITE, gcpu, ept_cr3_update); 586 event_gcpu_register(EVENT_GCPU_AFTER_GUEST_CR4_WRITE, gcpu, ept_cr4_update); 587 event_gcpu_register(EVENT_EMULATOR_AS_GUEST_ENTER, gcpu, ept_emulator_enter); 588 event_gcpu_register(EVENT_EMULATOR_AS_GUEST_LEAVE, gcpu, ept_emulator_exit); 589 event_gcpu_register(EVENT_GCPU_EPT_MISCONFIGURATION, gcpu, 590 ept_misconfiguration_vmexit); 591 event_gcpu_register(EVENT_GCPU_EPT_VIOLATION, gcpu, ept_violation_vmexit); 592 } 593 594 INLINE BOOLEAN ept_is_gcpu_active(IA32_VMX_VMCS_GUEST_SLEEP_STATE activity_state) 595 { 596 return ((Ia32VmxVmcsGuestSleepStateWaitForSipi != activity_state) && 597 ((Ia32VmxVmcsGuestSleepStateTripleFaultShutdown != activity_state))); 598 } 599 600 static void ept_gcpu_activity_state_change(GUEST_CPU_HANDLE gcpu, 601 EVENT_GCPU_ACTIVITY_STATE_CHANGE_DATA* pv) 602 { 603 const VIRTUAL_CPU_ID* vcpu_id = NULL; 604 EPT_GUEST_STATE *ept_guest = NULL; 605 606 #ifdef JLMDEBUG 607 bprint("ept_gcpu_activity_state_change\n"); 608 #endif 609 VMM_ASSERT( gcpu ); 610 VMM_ASSERT( pv ); 611 EPT_LOG("ept CPU#%d: activity state change: new state %d\r\n", 612 hw_cpu_id(), pv->new_state); 613 vcpu_id = guest_vcpu( gcpu ); 614 VMM_ASSERT(vcpu_id); 615 ept_guest = ept_find_guest_state(vcpu_id->guest_id); 616 VMM_ASSERT(ept_guest); 617 if (ept_is_gcpu_active(pv->new_state)) { 618 ept_guest_cpu_initialize(gcpu); 619 } 620 } 621 622 UINT32 ept_get_guest_address_width(GPM_HANDLE gpm) 623 { 624 GPM_RANGES_ITERATOR gpm_iter = 0; 625 GPA guest_range_addr = 0; 626 UINT64 guest_range_size = 0; 627 GPA guest_highest_range_addr = 0; 628 UINT64 guest_highest_range_size = 0; 629 UINT64 guest_address_limit = 0; 630 UINT32 guest_address_limit_msb_index = 0; 631 632 VMM_ASSERT(gpm); 633 gpm_iter = gpm_get_ranges_iterator(gpm); 634 while(GPM_INVALID_RANGES_ITERATOR != gpm_iter) { // for each range in GPM 635 gpm_iter = gpm_get_range_details_from_iterator(gpm, gpm_iter, 636 &guest_range_addr, &guest_range_size); 637 if(guest_range_addr > guest_highest_range_addr) { 638 guest_highest_range_addr = guest_range_addr; 639 guest_highest_range_size = guest_range_size; 640 } 641 } 642 guest_address_limit = guest_highest_range_addr + guest_highest_range_size; 643 hw_scan_bit_backward64(&guest_address_limit_msb_index, guest_address_limit); 644 return guest_address_limit_msb_index + 1; 645 } 646 647 MAM_HANDLE ept_create_guest_address_space(GPM_HANDLE gpm, BOOLEAN original_perms) 648 { 649 MAM_HANDLE address_space = NULL; 650 MAM_ATTRIBUTES attributes = {0}, hpa_attrs; 651 GPM_RANGES_ITERATOR gpm_iter = 0; 652 GPA guest_range_addr = 0; 653 UINT64 guest_range_size = 0; 654 HPA host_range_addr = 0; 655 BOOLEAN status = FALSE; 656 UINT64 same_memory_type_range_size = 0, covered_guest_range_size = 0; 657 VMM_PHYS_MEM_TYPE mem_type; 658 659 VMM_ASSERT(gpm); 660 661 // if (original_perms == FALSE) then permissions = RWX (default) 662 attributes.ept_attr.readable = 1; 663 attributes.ept_attr.writable = 1; 664 attributes.ept_attr.executable = 1; 665 666 address_space = mam_create_mapping(attributes); 667 VMM_ASSERT(address_space); 668 gpm_iter = gpm_get_ranges_iterator(gpm); 669 while(GPM_INVALID_RANGES_ITERATOR != gpm_iter) { // for each range in GPM 670 gpm_iter = gpm_get_range_details_from_iterator(gpm, gpm_iter, 671 &guest_range_addr, &guest_range_size); 672 status = gpm_gpa_to_hpa(gpm, guest_range_addr, &host_range_addr, &hpa_attrs); 673 if (original_perms) { 674 attributes.ept_attr.readable = hpa_attrs.ept_attr.readable; 675 attributes.ept_attr.writable = hpa_attrs.ept_attr.writable; 676 attributes.ept_attr.executable = hpa_attrs.ept_attr.executable; 677 } 678 if(status) { 679 covered_guest_range_size = 0; 680 do { // add separate mapping per memory type 681 mem_type = mtrrs_abstraction_get_range_memory_type( 682 host_range_addr + covered_guest_range_size, 683 &same_memory_type_range_size, 684 guest_range_size - covered_guest_range_size); 685 if (VMM_PHYS_MEM_UNDEFINED == mem_type) { 686 EPT_LOG(" EPT %s: Undefined mem-type for region %P. Use Uncached\n", 687 guest_range_addr + covered_guest_range_size); 688 mem_type = VMM_PHYS_MEM_UNCACHED; 689 } 690 attributes.ept_attr.emt = mem_type; 691 if(covered_guest_range_size + same_memory_type_range_size > guest_range_size) { 692 same_memory_type_range_size = guest_range_size - covered_guest_range_size; 693 } 694 mam_insert_range(address_space, guest_range_addr + covered_guest_range_size, 695 host_range_addr + covered_guest_range_size, same_memory_type_range_size, 696 attributes); 697 covered_guest_range_size += same_memory_type_range_size; 698 } while(covered_guest_range_size < guest_range_size); 699 } 700 } 701 return address_space; 702 } 703 704 void ept_invalidate_ept(CPU_ID from UNUSED, void* arg) 705 { 706 EPT_INVEPT_CMD *invept_cmd = (EPT_INVEPT_CMD *) arg; 707 708 #ifdef JLMDEBUG 709 bprint("ept_invalidate_ept\n"); 710 #endif 711 if (invept_cmd->host_cpu_id != ANY_CPU_ID && 712 invept_cmd->host_cpu_id != hw_cpu_id()) { 713 // not for this CPU -- ignore command 714 return; 715 } 716 switch(invept_cmd->cmd) { 717 case INVEPT_ALL_CONTEXTS: // Not being used currently 718 ept_hw_invept_all_contexts(); 719 break; 720 case INVEPT_CONTEXT_WIDE: 721 ept_hw_invept_context(invept_cmd->eptp); 722 break; 723 case INVEPT_INDIVIDUAL_ADDRESS: // Not being used currently 724 ept_hw_invept_individual_address(invept_cmd->eptp, invept_cmd->gpa); 725 break; 726 default: 727 VMM_ASSERT(0); 728 } 729 } 730 731 BOOLEAN ept_is_ept_supported(void) 732 { 733 return ept_hw_is_ept_supported(); 734 } 735 736 BOOLEAN ept_is_ept_enabled(GUEST_CPU_HANDLE gcpu) 737 { 738 return ept_hw_is_ept_enabled(gcpu); 739 } 740 741 UINT64 ept_compute_eptp(GUEST_HANDLE guest, UINT64 ept_root_table_hpa, UINT32 gaw) 742 { 743 EPTP eptp; 744 745 VMM_ASSERT(guest); 746 VMM_ASSERT(ept_root_table_hpa); 747 VMM_ASSERT(gaw); 748 eptp.Uint64 = ept_root_table_hpa; 749 eptp.Bits.GAW = ept_hw_get_guest_address_width_encoding(gaw); 750 eptp.Bits.ETMT = ept_hw_get_ept_memory_type(); 751 eptp.Bits.Reserved = 0; 752 return eptp.Uint64; 753 } 754 755 //NOTE: This function is expected to be always called with the lock acquired 756 BOOLEAN ept_enable(GUEST_CPU_HANDLE gcpu) 757 { 758 UINT64 ept_root_table_hpa = 0; 759 UINT32 gaw = 0; 760 761 VMM_ASSERT(gcpu); 762 ept_get_current_ept(gcpu, &ept_root_table_hpa, &gaw); 763 if (!ept_set_eptp(gcpu, ept_root_table_hpa, gaw)) { 764 EPT_PRINTERROR("EPT: failed to set eptp\r\n"); 765 goto failure; 766 } 767 if (!ept_hw_enable_ept(gcpu)) { 768 EPT_PRINTERROR("EPT: failed to enable ept\r\n"); 769 goto failure; 770 } 771 return TRUE; 772 773 failure: 774 return FALSE; 775 } 776 777 //NOTE: This function is expected to be always called with the lock acquired 778 void ept_disable(GUEST_CPU_HANDLE gcpu) 779 { 780 //ept_acquire_lock(); 781 ept_hw_disable_ept(gcpu); 782 //ept_release_lock(); 783 } 784 785 UINT64 ept_get_eptp(GUEST_CPU_HANDLE gcpu) 786 { 787 VMM_ASSERT(gcpu); 788 return ept_hw_get_eptp(gcpu); 789 } 790 791 BOOLEAN ept_set_eptp(GUEST_CPU_HANDLE gcpu, UINT64 ept_root_table_hpa, UINT32 gaw) 792 { 793 VMM_ASSERT(gcpu); 794 return ept_hw_set_eptp(gcpu, ept_root_table_hpa, gaw); 795 } 796 797 void ept_set_remote_eptp(CPU_ID from, void* arg) 798 { 799 EPT_SET_EPTP_CMD *set_eptp_cmd = arg; 800 GUEST_CPU_HANDLE gcpu; 801 (void)from; 802 gcpu = scheduler_get_current_gcpu_for_guest(set_eptp_cmd->guest_id); 803 if(gcpu == NULL || !ept_is_ept_enabled(gcpu)) { 804 return; 805 } 806 ept_set_eptp(gcpu, set_eptp_cmd->ept_root_table_hpa, set_eptp_cmd->gaw); 807 ept_invalidate_ept(ANY_CPU_ID, set_eptp_cmd->invept_cmd); 808 } 809 810 EPT_GUEST_STATE *ept_find_guest_state(GUEST_ID guest_id) 811 { 812 EPT_GUEST_STATE *ept_guest_state = NULL; 813 LIST_ELEMENT *iter = NULL; 814 BOOLEAN found = FALSE; 815 816 LIST_FOR_EACH(ept.guest_state, iter) { 817 ept_guest_state = LIST_ENTRY(iter, EPT_GUEST_STATE, list); 818 if(ept_guest_state->guest_id == guest_id) { 819 found = TRUE; 820 break; 821 } 822 } 823 if(found) { 824 return ept_guest_state; 825 } 826 return NULL; 827 } 828 829 static BOOLEAN ept_guest_initialize(GUEST_HANDLE guest) 830 { 831 UINT32 i; 832 EPT_GUEST_STATE *ept_guest = NULL; 833 834 #ifdef JLMDEBUG 835 bprint("ept_guest_initialize\n"); 836 #endif 837 ept_guest = (EPT_GUEST_STATE *) vmm_malloc(sizeof(EPT_GUEST_STATE)); 838 VMM_ASSERT(ept_guest); 839 ept_guest->guest_id = guest_get_id(guest); 840 list_add(ept.guest_state, ept_guest->list); 841 ept_guest->gcpu_state = (EPT_GUEST_CPU_STATE **) vmm_malloc(ept.num_of_cpus * sizeof(EPT_GUEST_CPU_STATE*)); 842 VMM_ASSERT(ept_guest->gcpu_state); 843 for (i = 0; i < ept.num_of_cpus; i++) { 844 ept_guest->gcpu_state[i] = (EPT_GUEST_CPU_STATE *) vmm_malloc(sizeof(EPT_GUEST_CPU_STATE)); 845 VMM_ASSERT(ept_guest->gcpu_state[i]); 846 } 847 event_global_register( EVENT_BEGIN_GPM_MODIFICATION_BEFORE_CPUS_STOPPED, 848 ept_begin_gpm_modification_before_cpus_stop); 849 event_global_register( EVENT_END_GPM_MODIFICATION_BEFORE_CPUS_RESUMED, 850 ept_end_gpm_modification_before_cpus_resume); 851 event_global_register( EVENT_END_GPM_MODIFICATION_AFTER_CPUS_RESUMED, 852 ept_end_gpm_modification_after_cpus_resume); 853 854 return TRUE; 855 } 856 857 static BOOLEAN ept_guest_cpu_initialize(GUEST_CPU_HANDLE gcpu) 858 { 859 const VIRTUAL_CPU_ID* vcpu_id = NULL; 860 EPT_GUEST_CPU_STATE *ept_guest_cpu = NULL; 861 EPT_GUEST_STATE *ept_guest_state = NULL; 862 863 EPT_LOG("EPT: CPU#%d ept_guest_cpu_initialize\r\n", hw_cpu_id()); 864 vcpu_id = guest_vcpu( gcpu ); 865 VMM_ASSERT(vcpu_id); 866 ept_guest_state = ept_find_guest_state(vcpu_id->guest_id); 867 VMM_ASSERT(ept_guest_state); 868 ept_guest_cpu = ept_guest_state->gcpu_state[vcpu_id->guest_cpu_id]; 869 //During S3 resume, these values need to be updated 870 ept_guest_cpu->cr0 = gcpu_get_guest_visible_control_reg(gcpu, IA32_CTRL_CR0); 871 ept_guest_cpu->cr4 = gcpu_get_guest_visible_control_reg(gcpu, IA32_CTRL_CR4); 872 if (!ept_guest_cpu->is_initialized) { 873 ept_register_events(gcpu); 874 ept_guest_cpu->is_initialized = TRUE; 875 } 876 return TRUE; 877 } 878 879 static void ept_fill_vmexit_request(VMEXIT_CONTROL *vmexit_request) 880 { 881 #ifdef JLMDEBUG 882 bprint("ept_fill_vmexit_request\n"); 883 #endif 884 vmm_zeromem(vmexit_request, sizeof(VMEXIT_CONTROL)); 885 if(!is_unrestricted_guest_supported()) { 886 vmexit_request->cr0.bit_request = CR0_PG; 887 vmexit_request->cr0.bit_mask = CR0_PG; 888 vmexit_request->cr4.bit_request = CR4_PAE; 889 vmexit_request->cr4.bit_mask = CR4_PAE; 890 } 891 } 892 893 static BOOLEAN ept_add_gcpu(GUEST_CPU_HANDLE gcpu, void *pv UNUSED) 894 { 895 EVENT_GCPU_ACTIVITY_STATE_CHANGE_DATA activity_state; 896 VMEXIT_CONTROL vmexit_request; 897 898 #ifdef JLMDEBUG 899 bprint("ept_add_gcpu\n"); 900 #endif 901 vmm_zeromem(&activity_state, sizeof(activity_state)); 902 vmm_zeromem(&vmexit_request, sizeof(vmexit_request)); 903 event_gcpu_register(EVENT_GCPU_ACTIVITY_STATE_CHANGE, gcpu, 904 (event_callback) ept_gcpu_activity_state_change); 905 activity_state.new_state = gcpu_get_activity_state(gcpu); 906 if(ept_is_gcpu_active(activity_state.new_state)) 907 {// if gcpu already active, fire manually 908 ept_gcpu_activity_state_change(gcpu, &activity_state); 909 } 910 // setup control only if gcpu is added on this host CPU 911 if(hw_cpu_id() == scheduler_get_host_cpu_id(gcpu)) 912 { 913 ept_fill_vmexit_request(&vmexit_request); 914 gcpu_control_setup(gcpu, &vmexit_request); 915 } 916 return TRUE; 917 } 918 919 static void ept_add_static_guest(GUEST_HANDLE guest) 920 { 921 GUEST_CPU_HANDLE gcpu; 922 GUEST_GCPU_ECONTEXT gcpu_context; 923 VMEXIT_CONTROL vmexit_request; 924 UINT64 ept_root_table_hpa = 0; 925 UINT32 ept_gaw = 0; 926 927 #ifdef JLMDEBUG 928 bprint("ept_add_static_guest\n"); 929 #endif 930 EPT_LOG("ept CPU#%d: activate ept\r\n", hw_cpu_id()); 931 ept_fill_vmexit_request(&vmexit_request); 932 // request needed vmexits 933 guest_control_setup(guest, &vmexit_request); 934 ept_guest_initialize(guest); 935 // Initialize default EPT 936 ept_create_default_ept(guest, guest_get_startup_gpm(guest)); 937 // Get default EPT 938 ept_get_default_ept(guest, &ept_root_table_hpa, &ept_gaw); 939 for(gcpu = guest_gcpu_first(guest, &gcpu_context); gcpu; gcpu = guest_gcpu_next(&gcpu_context)) { 940 ept_add_gcpu(gcpu, NULL); 941 // Set EPT pointer (of each GCPU) to default EPT 942 ept_set_current_ept(gcpu, ept_root_table_hpa, ept_gaw); 943 } 944 } 945 946 static BOOLEAN ept_add_dynamic_guest(GUEST_CPU_HANDLE gcpu UNUSED, void *pv) 947 { 948 EVENT_GUEST_CREATE_DATA *guest_create_event_data = (EVENT_GUEST_CREATE_DATA *) pv; 949 GUEST_HANDLE guest = guest_handle(guest_create_event_data->guest_id); 950 VMM_PAGING_POLICY pg_policy; 951 POL_RETVAL policy_status; 952 953 #ifdef JLMDEBUG 954 bprint("ept_add_dynamic_guest\n"); 955 #endif 956 policy_status = get_paging_policy(guest_policy(guest), &pg_policy); 957 VMM_ASSERT(POL_RETVAL_SUCCESS == policy_status); 958 if (POL_PG_EPT == pg_policy) { 959 ept_guest_initialize(guest_handle(guest_create_event_data->guest_id)); 960 } 961 return TRUE; 962 } 963 964 void init_ept_addon(UINT32 num_of_cpus) 965 { 966 GUEST_HANDLE guest; 967 GUEST_ECONTEXT guest_ctx; 968 969 if (!global_policy_uses_ept()) { 970 return; 971 } 972 vmm_zeromem(&ept, sizeof(ept)); 973 ept.num_of_cpus = num_of_cpus; 974 EPT_LOG("init_ept_addon: Initialize EPT num_cpus %d\n", num_of_cpus); 975 list_init(ept.guest_state); 976 lock_initialize(&ept.lock); 977 event_global_register(EVENT_GUEST_CREATE, ept_add_dynamic_guest); 978 event_global_register(EVENT_GCPU_ADD, (event_callback) ept_add_gcpu); 979 for(guest = guest_first(&guest_ctx); guest; guest = guest_next(&guest_ctx)) { 980 ept_add_static_guest(guest); 981 } 982 } 983 984 BOOLEAN ept_page_walk(UINT64 first_table, UINT64 addr, UINT32 gaw) 985 { 986 UINT64 *table = (UINT64 *) first_table; 987 UINT64 *entry = NULL; 988 989 EPT_LOG("EPT page walk addr %p\r\n", addr); 990 if(gaw > 39) { 991 entry = &table[(addr & 0xFF8000000000) >> 39]; 992 EPT_LOG("Level 4: table %p entry %p\r\n", table, *entry); 993 table = (UINT64 *) ((*entry) & ~0xfff); 994 if(((*entry) & 0x1) == 0) { 995 EPT_LOG("Entry not present\r\n"); 996 return FALSE; 997 } 998 } 999 entry = &table[(addr & 0x7fc0000000) >> 30]; 1000 EPT_LOG("Level 3: table %p entry %p\r\n", table, *entry); 1001 if(((*entry) & 0x1) == 0) { 1002 EPT_LOG("Entry not present\r\n"); 1003 return FALSE; 1004 } 1005 table = (UINT64 *) ((*entry) & ~0xfff); 1006 entry = &table[(addr & 0x3FE00000) >> 21]; 1007 EPT_LOG("Level 2: table %p entry %p\r\n", table, *entry); 1008 table = (UINT64 *) ((*entry) & ~0xfff); 1009 if(((*entry) & 0x1) == 0) { 1010 EPT_LOG("Entry not present\r\n"); 1011 return FALSE; 1012 } 1013 entry = &table[(addr & 0x1ff000) >> 12]; 1014 EPT_LOG("Level 1: table %p entry %p\r\n", table, *entry); 1015 return TRUE; 1016 } 1017 1018 #ifdef DEBUG 1019 void ept_print(IN GUEST_HANDLE guest, IN MAM_HANDLE address_space) 1020 { 1021 MAM_MEMORY_RANGES_ITERATOR iter; 1022 MAM_MAPPING_RESULT res; 1023 1024 iter = mam_get_memory_ranges_iterator(address_space); 1025 1026 while (iter != MAM_INVALID_MEMORY_RANGES_ITERATOR) { 1027 GPA curr_gpa; 1028 UINT64 curr_size; 1029 HPA curr_hpa; 1030 MAM_ATTRIBUTES attrs; 1031 iter = mam_get_range_details_from_iterator(address_space, iter, 1032 (UINT64*)&curr_gpa, &curr_size); 1033 VMM_ASSERT(curr_size != 0); 1034 1035 res = mam_get_mapping(address_space, curr_gpa, &curr_hpa, &attrs); 1036 if (res == MAM_MAPPING_SUCCESSFUL) { 1037 EPT_LOG("EPT guest#%d: GPA %p -> HPA %p\r\n", curr_gpa, curr_hpa); 1038 } 1039 } 1040 } 1041 #endif 1042 1043 #ifdef INCLUDE_UNUSED_CODE 1044 static 1045 void ept_reset_initiate(GUEST_HANDLE guest) 1046 { 1047 GUEST_CPU_HANDLE gcpu; 1048 1049 VMM_ASSERT(guest); 1050 gcpu = scheduler_get_current_gcpu_for_guest(guest_get_id(guest)); 1051 1052 if(gcpu != NULL && ept_is_ept_enabled(gcpu)) { 1053 ept_disable(gcpu); 1054 ept_enable(gcpu); 1055 } 1056 } 1057 1058 void ept_single_cpu_update(GUEST_HANDLE guest, TMSL_MEM_VIEW_HANDLE handle) 1059 { 1060 EPT_INVEPT_CMD invept_cmd; 1061 1062 #ifdef JLMDEBUG 1063 bprint("ept_single_cpu_update\n"); 1064 #endif 1065 invept_cmd.host_cpu_id = ANY_CPU_ID; 1066 invept_cmd.cmd = INVEPT_CONTEXT_WIDE; 1067 invept_cmd.eptp = ept_compute_eptp(guest, handle); 1068 ept_invalidate_ept(ANY_CPU_ID, &invept_cmd); 1069 } 1070 1071 static void ept_reset_local(CPU_ID from UNUSED, void* arg) 1072 { 1073 GUEST_HANDLE guest = (GUEST_HANDLE) arg; 1074 GUEST_CPU_HANDLE gcpu; 1075 1076 VMM_ASSERT(guest); 1077 gcpu = scheduler_get_current_gcpu_for_guest(guest_get_id(guest)); 1078 if(gcpu != NULL && ept_is_ept_enabled(gcpu)) { 1079 ept_disable(gcpu); 1080 ept_enable(gcpu); 1081 } 1082 } 1083 1084 #endif 1085 1086 #ifdef INCLUDE_UNUSED_CODE 1087 static void ept_exec_invept(CPU_ID dest, INVEPT_CMD_TYPE cmd, 1088 UINT64 eptp, UINT64 gpa) 1089 { 1090 IPC_DESTINATION ipc_dest; 1091 EPT_INVEPT_CMD invept_cmd; 1092 1093 vmm_zeromem(&ipc_dest, sizeof(ipc_dest)); 1094 vmm_zeromem(&invept_cmd, sizeof(invept_cmd)); 1095 1096 invept_cmd.host_cpu_id = dest; 1097 invept_cmd.cmd = cmd; 1098 invept_cmd.eptp = eptp; 1099 invept_cmd.gpa = gpa; 1100 ipc_dest.addr_shorthand = LOCAL_APIC_BROADCAST_MODE_ALL_EXCLUDING_SELF; 1101 ipc_execute_handler_sync(ipc_dest, ept_invalidate_ept, (void *) &invept_cmd); 1102 } 1103 1104 void ept_invalidate_guest_ept_on_all_cpus(IN GUEST_HANDLE guest) 1105 { 1106 UINT64 eptp = 0; 1107 1108 ept_acquire_lock(); 1109 eptp = ept_compute_eptp(guest); 1110 ept_exec_invept(ANY_CPU_ID, INVEPT_CONTEXT_WIDE, eptp, 0); 1111 EPT_LOG("Invalidate eptp %p on CPU#%d\r\n", eptp, hw_cpu_id()); 1112 ept_hw_invept_context(eptp); 1113 ept_release_lock(); 1114 } 1115 1116 BOOLEAN ept_invept_all_contexts(IN CPU_ID host_cpu_id) 1117 { 1118 BOOLEAN res = FALSE; 1119 1120 ept_acquire_lock(); 1121 if(host_cpu_id == hw_cpu_id()) { 1122 res = ept_hw_invept_all_contexts(); 1123 } 1124 else { 1125 ept_exec_invept(ANY_CPU_ID, INVEPT_ALL_CONTEXTS, 0, 0); 1126 } 1127 ept_release_lock(); 1128 return res; 1129 } 1130 1131 BOOLEAN ept_invept_context(IN CPU_ID host_cpu_id, UINT64 eptp) 1132 { 1133 BOOLEAN res = FALSE; 1134 1135 ept_acquire_lock(); 1136 if(host_cpu_id == hw_cpu_id()) { 1137 res = ept_hw_invept_context(eptp); 1138 } 1139 else { 1140 ept_exec_invept(ANY_CPU_ID, INVEPT_CONTEXT_WIDE, eptp, 0); 1141 } 1142 ept_release_lock(); 1143 return res; 1144 } 1145 1146 BOOLEAN ept_invept_individual_address(IN CPU_ID host_cpu_id, UINT64 eptp, ADDRESS gpa) 1147 { 1148 BOOLEAN res = FALSE; 1149 1150 ept_acquire_lock(); 1151 if(host_cpu_id == hw_cpu_id()) { 1152 res = ept_hw_invept_individual_address(eptp, gpa); 1153 } 1154 else { 1155 ept_exec_invept(ANY_CPU_ID, INVEPT_INDIVIDUAL_ADDRESS, eptp, gpa); 1156 } 1157 ept_release_lock(); 1158 return res; 1159 } 1160 #endif 1161 1162 #ifdef INCLUDE_UNUSED_CODE 1163 BOOLEAN ept_add_mapping(IN GUEST_HANDLE guest, IN GPA src, IN HPA dest, IN UINT64 size, 1164 IN BOOLEAN readable, IN BOOLEAN writable, IN BOOLEAN executable) 1165 { 1166 EPT_GUEST_STATE *ept_guest = NULL; 1167 UINT64 eptp = 0; 1168 GUEST_ID guest_id = guest_get_id(guest); 1169 MAM_ATTRIBUTES attrs; 1170 BOOLEAN status = FALSE; 1171 EPT_INVEPT_CMD invept_cmd; 1172 1173 VMM_ASSERT( guest ); 1174 vmm_zeromem(&invept_cmd, sizeof(invept_cmd)); 1175 ept_guest = ept_find_guest_state(guest_id); 1176 VMM_ASSERT(ept_guest); 1177 ept_acquire_lock(); 1178 stop_all_cpus(); 1179 attrs.uint32 = 0; 1180 attrs.ept_attr.readable = readable; 1181 attrs.ept_attr.writable = writable; 1182 attrs.ept_attr.executable = executable; 1183 status = mam_insert_range(ept_guest->address_space, src, dest, size, attrs); 1184 eptp = ept_compute_eptp(guest); 1185 invept_cmd.host_cpu_id = ANY_CPU_ID; 1186 invept_cmd.cmd = INVEPT_CONTEXT_WIDE; 1187 invept_cmd.eptp = eptp; 1188 start_all_cpus(ept_invalidate_ept, (void *) &invept_cmd); 1189 ept_hw_invept_context(eptp); 1190 ept_release_lock(); 1191 return status; 1192 } 1193 1194 BOOLEAN ept_remove_mapping(IN GUEST_HANDLE guest, IN GPA src, 1195 IN UINT64 size, IN MAM_MAPPING_RESULT reason) 1196 { 1197 EPT_GUEST_STATE *ept_guest = NULL; 1198 UINT64 eptp = 0; 1199 GUEST_ID guest_id = guest_get_id(guest); 1200 BOOLEAN status = FALSE; 1201 EPT_INVEPT_CMD invept_cmd; 1202 1203 VMM_ASSERT( guest ); 1204 ept_guest = ept_find_guest_state(guest_id); 1205 VMM_ASSERT(ept_guest); 1206 ept_acquire_lock(); 1207 stop_all_cpus(); 1208 status = mam_insert_not_existing_range(ept_guest->address_space, src, size, reason); 1209 eptp = ept_compute_eptp(guest); 1210 vmm_zeromem(&invept_cmd, sizeof(invept_cmd)); 1211 invept_cmd.host_cpu_id = ANY_CPU_ID; 1212 invept_cmd.cmd = INVEPT_CONTEXT_WIDE; 1213 invept_cmd.eptp = eptp; 1214 start_all_cpus(ept_invalidate_ept, (void *) &invept_cmd); 1215 ept_hw_invept_context(eptp); 1216 ept_release_lock(); 1217 return status; 1218 } 1219 1220 MAM_MAPPING_RESULT ept_get_mapping(IN GUEST_HANDLE guest, IN GPA src, 1221 OUT HPA *dest, OUT MAM_ATTRIBUTES *attrs) 1222 { 1223 EPT_GUEST_STATE *ept_guest = NULL; 1224 GUEST_ID guest_id = guest_get_id(guest); 1225 MAM_MAPPING_RESULT res; 1226 1227 ept_guest = ept_find_guest_state(guest_id); 1228 VMM_ASSERT(ept_guest); 1229 ept_acquire_lock(); 1230 res = mam_get_mapping(ept_guest, src, dest, attrs); 1231 ept_release_lock(); 1232 return res; 1233 } 1234 1235 static BOOLEAN ept_allow_uvmm_heap_access(GUEST_CPU_HANDLE gcpu) 1236 { 1237 GUEST_HANDLE guest = NULL; 1238 HVA heap_base_hva = 0; 1239 HPA heap_base_hpa = 0; 1240 UINT32 heap_size = 0; 1241 VMM_PHYS_MEM_TYPE mem_type; 1242 BOOLEAN status = FALSE; 1243 UINT64 same_memory_type_range_size = 0, covered_heap_range_size = 0; 1244 MAM_ATTRIBUTES attributes = {0}; 1245 EPT_GUEST_STATE *ept_guest = NULL; 1246 1247 guest = gcpu_guest_handle(gcpu); 1248 ept_guest = ept_find_guest_state(guest_get_id(guest)); 1249 VMM_ASSERT(ept_guest); 1250 vmm_heap_get_details(&heap_base_hva, &heap_size); 1251 status = hmm_hva_to_hpa(heap_base_hva, &heap_base_hpa); 1252 VMM_ASSERT(status); 1253 attributes.ept_attr.readable = 1; 1254 attributes.ept_attr.writable = 1; 1255 while(covered_heap_range_size < heap_size) { 1256 mem_type = mtrrs_abstraction_get_range_memory_type( 1257 heap_base_hpa + covered_heap_range_size, 1258 &same_memory_type_range_size); 1259 attributes.ept_attr.emt = mem_type; 1260 EPT_LOG(" EPT add uvmm heap range: gpa %p -> hpa %p; size %p; mem_type %d\r\n", 1261 heap_base_hpa, heap_base_hpa, same_memory_type_range_size, mem_type); 1262 1263 if(covered_heap_range_size + same_memory_type_range_size > heap_size) { // normalize 1264 same_memory_type_range_size = heap_size - covered_heap_range_size; 1265 } 1266 ept_add_mapping(guest, heap_base_hpa + covered_heap_range_size, 1267 heap_base_hpa + covered_heap_range_size, same_memory_type_range_size, 1268 TRUE, // readable 1269 TRUE, // writable 1270 FALSE // executable 1271 ); 1272 covered_heap_range_size += same_memory_type_range_size; 1273 if(covered_heap_range_size > heap_size) { // normalize 1274 covered_heap_range_size = heap_size; 1275 } 1276 } 1277 return TRUE; 1278 } 1279 1280 static BOOLEAN ept_deny_uvmm_heap_access(GUEST_CPU_HANDLE gcpu) 1281 { 1282 GUEST_HANDLE guest = NULL; 1283 HVA heap_base_hva = 0; 1284 HPA heap_base_hpa = 0; 1285 UINT32 heap_size = 0; 1286 BOOLEAN status = FALSE; 1287 UINT32 i = 0; 1288 EPT_GUEST_STATE *ept_guest = NULL; 1289 EPT_GUEST_CPU_STATE *ept_guest_cpu = NULL; 1290 const VIRTUAL_CPU_ID* vcpu_id = NULL; 1291 1292 VMM_ASSERT( gcpu ); 1293 vcpu_id = guest_vcpu( gcpu ); 1294 ept_guest = ept_find_guest_state(vcpu_id->guest_id); 1295 VMM_ASSERT(ept_guest); 1296 1297 for(i = 0; i < ept.num_of_cpus; i++) { 1298 ept_guest_cpu = ept_guest->gcpu_state[i]; 1299 if(ept_guest_cpu->is_initialized 1300 && (ept_guest_cpu->cr0 & CR0_PG) == 0) { // cannot deny access - another gcpu not paged and uses flat page tables 1301 return FALSE; 1302 } 1303 } 1304 guest = gcpu_guest_handle(gcpu); 1305 vmm_heap_get_details(&heap_base_hva, &heap_size); 1306 status = hmm_hva_to_hpa(heap_base_hva, &heap_base_hpa); 1307 VMM_ASSERT(status); 1308 EPT_LOG(" EPT remove uvmm heap range: gpa %p -> hpa %p; size %p;\r\n", 1309 heap_base_hpa, heap_base_hpa, heap_size); 1310 ept_remove_mapping(guest, heap_base_hpa, heap_size, 0); 1311 return TRUE; 1312 } 1313 #endif