git: d400b1ca31fd - main - vmm: Synchronize long-mode state when emulating CR0 writes
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 21 Sep 2026 16:41:24 UTC
The branch main has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=d400b1ca31fd9b12655c83f9a7b3e8cdb68646dd
commit d400b1ca31fd9b12655c83f9a7b3e8cdb68646dd
Author: Hayzam Sherif <hayzam@gmail.com>
AuthorDate: 2026-09-21 16:33:55 +0000
Commit: Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-09-21 16:36:06 +0000
vmm: Synchronize long-mode state when emulating CR0 writes
vmx_emulate_cr0_access() sets EFER.LMA and the IA-32e guest VM-entry
control when enabling paging with EFER.LME set, but does not clear them
when disabling paging. This can leave an inconsistent guest state that
fails VM entry.
Update both fields in either direction based on EFER.LME and the CR0
value written to the VMCS. Use the mask-adjusted CR0 value so the
resulting state remains consistent with the VMX fixed-bit requirements.
Reviewed by: markj
MFC after: 2 weeks
Sponsored by: The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D59756
---
sys/amd64/vmm/intel/vmx.c | 32 +++++++++++++++-----------------
1 file changed, 15 insertions(+), 17 deletions(-)
diff --git a/sys/amd64/vmm/intel/vmx.c b/sys/amd64/vmm/intel/vmx.c
index 65a6d9603caf..29aadb5881f3 100644
--- a/sys/amd64/vmm/intel/vmx.c
+++ b/sys/amd64/vmm/intel/vmx.c
@@ -1879,7 +1879,7 @@ vmx_set_guest_reg(struct vmx_vcpu *vcpu, int ident, uint64_t regval)
static int
vmx_emulate_cr0_access(struct vmx_vcpu *vcpu, uint64_t exitqual)
{
- uint64_t crval, regval;
+ uint64_t crval, efer, entry_ctls, regval;
/* We only handle mov to %cr0 at this time */
if ((exitqual & 0xf0) != 0x00)
@@ -1893,23 +1893,21 @@ vmx_emulate_cr0_access(struct vmx_vcpu *vcpu, uint64_t exitqual)
crval &= ~cr0_zeros_mask;
vmcs_write(VMCS_GUEST_CR0, crval);
- if (regval & CR0_PG) {
- uint64_t efer, entry_ctls;
-
- /*
- * If CR0.PG is 1 and EFER.LME is 1 then EFER.LMA and
- * the "IA-32e mode guest" bit in VM-entry control must be
- * equal.
- */
- efer = vmcs_read(VMCS_GUEST_IA32_EFER);
- if (efer & EFER_LME) {
- efer |= EFER_LMA;
- vmcs_write(VMCS_GUEST_IA32_EFER, efer);
- entry_ctls = vmcs_read(VMCS_ENTRY_CTLS);
- entry_ctls |= VM_ENTRY_GUEST_LMA;
- vmcs_write(VMCS_ENTRY_CTLS, entry_ctls);
- }
+ /*
+ * Keep EFER.LMA and the IA-32e guest VM-entry control in sync
+ * with CR0.PG, using the value written to the VMCS (crval).
+ */
+ efer = vmcs_read(VMCS_GUEST_IA32_EFER);
+ entry_ctls = vmcs_read(VMCS_ENTRY_CTLS);
+ if ((crval & CR0_PG) != 0 && (efer & EFER_LME) != 0) {
+ efer |= EFER_LMA;
+ entry_ctls |= VM_ENTRY_GUEST_LMA;
+ } else {
+ efer &= ~EFER_LMA;
+ entry_ctls &= ~VM_ENTRY_GUEST_LMA;
}
+ vmcs_write(VMCS_GUEST_IA32_EFER, efer);
+ vmcs_write(VMCS_ENTRY_CTLS, entry_ctls);
return (HANDLED);
}