Ejemplo n.º 1
0
/*
 * 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);
	}
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
0
/*
 * 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);
	}
}