In VMX operation, the value of guest CR0/CR4 need to consider the following cases:

  1. processors may fix certain bits in CR0 and CR4 to specific values and not support other values.
  2. Guest/Host Masks and Read Shadows for CR0 and CR4

1. Background

For the first case, you can refer to intel SDM Vol. 3D A.7(VMX-FIXED BITS IN CR0) and A.8(VMX-FIXED BITS IN CR4).

We’ll deep into the second case.

From the description, please try to answer this question: Why needs the guest/host masks and read shadow?

Let’s consider this example:

From SDM description, CR4.VMXE must be 1 in VMX operation. Without guest/host masks and read shadow, if guest is a normal operating system, not a VMM, when guest want to get CR4.VMXE, it would be 1. That’s the wrong value for guest. But we still need to ensure physical CR4.VMXE is1 in non-root mode!

How can we satisfy all the above requirements?

guest/host masks and read shadow help us solve the issue.

  1. set VMCS Guest CR4(00006804H ) field VMXE bit to be 1, to ensure physical CR4.VMXE is 1 in non-root mode;

  2. set CR4 guest/host masks(00006002H ) field VMXE bit to be 1, means VMXE bit is owned by host;

  3. set CR4 read shadow(00006006H ) field VMXE bit to be 0.

When guest reads CR4.VMXE, hardware will return value for this bit from the corresponding read shadow(the value is 0 here). While physical CR4.VMXE still is 1 in non-root mode.

2. Example

Let’s take ACRN virtual_cr.c as an example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/*
* Physical CR4 bits in VMX operation may be either flexible or fixed.
* Guest CR4 bits may be operatable or reserved.
*
* All the guest reserved bits should be TRAPed and EMULATed by HV
* (inject #GP).
*
* For guest operatable bits, it may be:
* CR4_PASSTHRU_BITS:
* Bits that may be passed through to guest. The actual passthru bits
* should be masked by flexible bits.
*
* CR4_TRAP_AND_PASSTHRU_BITS:
* The bits are trapped by HV and HV emulation will eventually write
* the guest value to physical CR4 (GUEST_CR4) too. The actual bits
* should be masked by flexible bits.
*
* CR4_TRAP_AND_EMULATE_BITS:
* The bits are trapped by HV and emulated, but HV updates vCR4 only
* (no update to physical CR4), i.e. pure software emulation.
*
* CR4_EMULATED_RESERVE_BITS:
* The bits are trapped, but are emulated by injecting a #GP.
*
* NOTE: Above bits should not overlap.
*
*/
1
#define CR0_TRAP_AND_PASSTHRU_BITS	(CR0_PE | CR0_PG | CR0_WP)

CR0_PG is trapped by HV and HV emulation will eventually write the guest value to physical CR0 (GUEST_CR0) too. Can we pass through CR0_PG to guest? The answer is no!

1
2
3
4
5
6
7
8
9
10
11
12
13
if ((cr0_changed_bits & CR0_PG) != 0UL) {
/* PG bit changes */
if ((effective_cr0 & CR0_PG) != 0UL) {
/* Enable paging */
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);
}

Here’s the reason why we set theVMX_ENTRY_CTLS_IA32E_MODE and MSR_IA32_EFER_LMA_BIT.

If pass through CR0_PG to guest, while guest runs in non-root mode, hardware couldn’t updateVMX_ENTRY_CONTROLS field. So we should trap CR0_PG, and HV will updateVMX_ENTRY_CONTROLS field in root mode.

3. Conclusion

If one bit has restriction in VMX operation or needs to do some operations in root mode, It’s better to trap(owned by host) this bit.