/* * Handling of CR4: * Assume "unrestricted guest" feature is supported by vmx. * * For CR4, if some feature is not supported by hardware, the corresponding bit * will be set in cr4_always_off_mask. If guest try to set these bits after * vmexit, will inject a #GP. * If a bit for a feature not supported by hardware, which is flexible to guest, * and write to it do not lead to a VM exit, a #GP should be generated inside * guest. * * - VME (0) Flexible to guest * - PVI (1) Flexible to guest * - TSD (2) Flexible to guest * - DE (3) Flexible to guest * - PSE (4) Trapped to track paging mode. * Set the value according to the value from guest. * - PAE (5) Trapped to track paging mode. * Set the value according to the value from guest. * - MCE (6) Trapped to hide from guest * - PGE (7) Flexible to guest * - PCE (8) Flexible to guest * - OSFXSR (9) Flexible to guest * - OSXMMEXCPT (10) Flexible to guest * - VMXE (13) Trapped to hide from guest * - SMXE (14) must always be 0 => must lead to a VM exit * - PCIDE (17) Trapped to hide from guest * - OSXSAVE (18) Flexible to guest * - XSAVE (19) Flexible to guest * We always keep align with physical cpu. So it's flexible to * guest * - SMEP (20) Flexible to guest * - SMAP (21) Flexible to guest * - PKE (22) Flexible to guest */ static void vmx_write_cr4(struct acrn_vcpu *vcpu, uint64_t cr4) { if (!is_cr4_write_valid(vcpu, cr4)) { pr_dbg("Invalid cr4 write operation from guest"); vcpu_inject_gp(vcpu, 0U); } else { uint64_t cr4_vmx, cr4_shadow; uint64_t old_cr4 = vcpu_get_cr4(vcpu); if (((cr4 ^ old_cr4) & (CR4_PGE | CR4_PSE | CR4_PAE | CR4_SMEP | CR4_SMAP | CR4_PKE)) != 0UL) { if (((cr4 & CR4_PAE) != 0UL) && (is_paging_enabled(vcpu)) && (is_long_mode(vcpu))) { load_pdptrs(vcpu); } vcpu_make_request(vcpu, ACRN_REQUEST_EPT_FLUSH); } /* Clear forced off bits */ cr4_shadow = cr4 & ~CR4_MCE; cr4_vmx = cr4_always_on_mask | cr4_shadow; exec_vmwrite(VMX_GUEST_CR4, cr4_vmx & 0xFFFFFFFFUL); exec_vmwrite(VMX_CR4_READ_SHADOW, cr4_shadow & 0xFFFFFFFFUL); /* clear read cache, next time read should from VMCS */ bitmap_clear_lock(CPU_REG_CR4, &vcpu->reg_cached); pr_dbg("VMM: Try to write %016llx, allow to write 0x%016llx to CR4", cr4, cr4_vmx); } }
static int submit_instruction_pir(struct target *t, int num) { LOG_DEBUG("%s op=0x%016" PRIx64, instructions[num].name, instructions[num].op); /* * TODO: switch off the CR0.PG bit * is not disabled by default here in order to make reads faster * but should be done before submitting an instruction * maybe recommended to do it in halt_prep() */ if (is_paging_enabled(t)) LOG_DEBUG("%s CR0.PG not disable: possible instruction fault", __func__); int err = submit_pir(t, instructions[num].op); if (err != ERROR_OK) { LOG_ERROR("%s error submitting pir", __func__); return err; } return err; }
/* * Handling of CR0: * Assume "unrestricted guest" feature is supported by vmx. * For mode switch, hv only needs to take care of enabling/disabling long mode, * thanks to "unrestricted guest" feature. * * - PE (0) Trapped to track cpu mode. * Set the value according to the value from guest. * - MP (1) Flexible to guest * - EM (2) Flexible to guest * - TS (3) Flexible to guest * - ET (4) Flexible to guest * - NE (5) must always be 1 * - WP (16) Trapped to get if it inhibits supervisor level procedures to * write into ro-pages. * - AM (18) Flexible to guest * - NW (29) Trapped to emulate cache disable situation * - CD (30) Trapped to emulate cache disable situation * - PG (31) Trapped to track cpu/paging mode. * Set the value according to the value from guest. */ static void vmx_write_cr0(struct acrn_vcpu *vcpu, uint64_t cr0) { if (!is_cr0_write_valid(vcpu, cr0)) { pr_dbg("Invalid cr0 write operation from guest"); vcpu_inject_gp(vcpu, 0U); } else { uint64_t cr0_vmx; uint32_t entry_ctrls; bool old_paging_enabled = is_paging_enabled(vcpu); uint64_t cr0_changed_bits = vcpu_get_cr0(vcpu) ^ cr0; uint64_t cr0_mask = cr0; /* SDM 2.5 * When loading a control register, reserved bit should always set * to the value previously read. */ cr0_mask &= ~CR0_RESERVED_MASK; if (!old_paging_enabled && ((cr0_mask & CR0_PG) != 0UL)) { if ((vcpu_get_efer(vcpu) & MSR_IA32_EFER_LME_BIT) != 0UL) { /* Enable long mode */ pr_dbg("VMM: Enable long mode"); entry_ctrls = exec_vmread32(VMX_ENTRY_CONTROLS); entry_ctrls |= VMX_ENTRY_CTLS_IA32E_MODE; exec_vmwrite32(VMX_ENTRY_CONTROLS, entry_ctrls); vcpu_set_efer(vcpu, vcpu_get_efer(vcpu) | MSR_IA32_EFER_LMA_BIT); } else if (is_pae(vcpu)) { /* enabled PAE from paging disabled */ load_pdptrs(vcpu); } else { /* do nothing */ } } else if (old_paging_enabled && ((cr0_mask & CR0_PG) == 0UL)) { if ((vcpu_get_efer(vcpu) & MSR_IA32_EFER_LME_BIT) != 0UL) { /* Disable long mode */ pr_dbg("VMM: Disable long mode"); entry_ctrls = exec_vmread32(VMX_ENTRY_CONTROLS); entry_ctrls &= ~VMX_ENTRY_CTLS_IA32E_MODE; exec_vmwrite32(VMX_ENTRY_CONTROLS, entry_ctrls); vcpu_set_efer(vcpu, vcpu_get_efer(vcpu) & ~MSR_IA32_EFER_LMA_BIT); } } else { /* do nothing */ } /* If CR0.CD or CR0.NW get cr0_changed_bits */ if ((cr0_changed_bits & (CR0_CD | CR0_NW)) != 0UL) { /* No action if only CR0.NW is cr0_changed_bits */ if ((cr0_changed_bits & CR0_CD) != 0UL) { if ((cr0_mask & CR0_CD) != 0UL) { /* * When the guest requests to set CR0.CD, we don't allow * guest's CR0.CD to be actually set, instead, we write guest * IA32_PAT with all-UC entries to emulate the cache * disabled behavior */ exec_vmwrite64(VMX_GUEST_IA32_PAT_FULL, PAT_ALL_UC_VALUE); if (!iommu_snoop_supported(vcpu->vm->iommu)) { cache_flush_invalidate_all(); } } else { /* Restore IA32_PAT to enable cache again */ exec_vmwrite64(VMX_GUEST_IA32_PAT_FULL, vcpu_get_guest_msr(vcpu, MSR_IA32_PAT)); } vcpu_make_request(vcpu, ACRN_REQUEST_EPT_FLUSH); } } if ((cr0_changed_bits & (CR0_PG | CR0_WP)) != 0UL) { vcpu_make_request(vcpu, ACRN_REQUEST_EPT_FLUSH); } /* CR0 has no always off bits, except the always on bits, and reserved * bits, allow to set according to guest. */ cr0_vmx = cr0_always_on_mask | cr0_mask; /* Don't set CD or NW bit to guest */ cr0_vmx &= ~(CR0_CD | CR0_NW); exec_vmwrite(VMX_GUEST_CR0, cr0_vmx & 0xFFFFFFFFUL); exec_vmwrite(VMX_CR0_READ_SHADOW, cr0_mask & 0xFFFFFFFFUL); /* clear read cache, next time read should from VMCS */ bitmap_clear_lock(CPU_REG_CR0, &vcpu->reg_cached); pr_dbg("VMM: Try to write %016llx, allow to write 0x%016llx to CR0", cr0_mask, cr0_vmx); } }