struct desc_table_reg linux_idtr;
unsigned long linux_reg[NUM_ENTRY_REGS];
unsigned long linux_ip;
+ unsigned long linux_cr0;
unsigned long linux_cr3;
struct segment linux_cs;
struct segment linux_ds;
#include <asm/percpu.h>
#include <asm/processor.h>
+#define X86_CR0_HOST_STATE \
+ (X86_CR0_PG | X86_CR0_WP | X86_CR0_NE | X86_CR0_ET | X86_CR0_TS | \
+ X86_CR0_MP | X86_CR0_PE)
+
struct vcpu_io_bitmap {
u8 *data;
u32 size;
gdt[GDT_DESC_TSS] &= ~DESC_TSS_BUSY;
asm volatile("ltr %%ax" : : "a" (GDT_DESC_TSS * 8));
+ cpu_data->linux_cr0 = read_cr0();
+
/* swap CR3 */
cpu_data->linux_cr3 = read_cr3();
write_cr3(paging_hvirt2phys(hv_paging_structs.root_table));
vcpu_exit(cpu_data);
write_msr(MSR_EFER, cpu_data->linux_efer);
+ write_cr0(cpu_data->linux_cr0);
write_cr3(cpu_data->linux_cr3);
asm volatile("lgdtq %0" : : "m" (cpu_data->linux_gdtr));
memset(vmcb, 0, sizeof(struct vmcb));
- vmcb->cr0 = read_cr0() & SVM_CR0_ALLOWED_BITS;
+ vmcb->cr0 = cpu_data->linux_cr0 & SVM_CR0_ALLOWED_BITS;
vmcb->cr3 = cpu_data->linux_cr3;
vmcb->cr4 = read_cr4();
if (!vmcb_setup(cpu_data))
return -EIO;
+ /*
+ * APM Volume 2, 3.1.1: "When writing the CR0 register, software should
+ * set the values of reserved bits to the values found during the
+ * previous CR0 read."
+ * But we want to avoid surprises with new features unknown to us but
+ * set by Linux. So check if any assumed revered bit was set and bail
+ * out if so.
+ */
+ if (cpu_data->linux_cr0 & X86_CR0_RESERVED)
+ return -EIO;
+
+ /* bring CR0 into well-defined state */
+ write_cr0(X86_CR0_HOST_STATE);
+
write_msr(MSR_VM_HSAVE_PA, paging_hvirt2phys(cpu_data->host_state));
return 0;
write_msr(MSR_KERNGS_BASE, vmcb->kerngsbase);
write_msr(MSR_IA32_PAT, vmcb->g_pat);
+ cpu_data->linux_cr0 = vmcb->cr0;
cpu_data->linux_cr3 = vmcb->cr3;
cpu_data->linux_gdtr.base = vmcb->gdtr.base;
sizeof(cpu_data->stack));
ok &= vmcs_write64(HOST_RIP, (unsigned long)vmx_vmexit);
- ok &= vmx_set_guest_cr(CR0_IDX, read_cr0());
+ ok &= vmx_set_guest_cr(CR0_IDX, cpu_data->linux_cr0);
ok &= vmx_set_guest_cr(CR4_IDX, read_cr4());
ok &= vmcs_write64(GUEST_CR3, cpu_data->linux_cr3);
cpu_data->vmcs.revision_id = revision_id;
cpu_data->vmcs.shadow_indicator = 0;
- // TODO: validate CR0
-
/* Note: We assume that TXT is off */
feature_ctrl = read_msr(MSR_IA32_FEATURE_CONTROL);
mask = FEATURE_CONTROL_LOCKED |
write_msr(MSR_IA32_FEATURE_CONTROL, feature_ctrl);
}
+ /*
+ * SDM Volume 3, 2.5: "When loading a control register, reserved bits
+ * should always be set to the values previously read."
+ * But we want to avoid surprises with new features unknown to us but
+ * set by Linux. So check if any assumed revered bit was set or should
+ * be set for VMX operation and bail out if so.
+ */
+ if ((cpu_data->linux_cr0 | cr_required1[CR0_IDX]) & X86_CR0_RESERVED)
+ return -EIO;
+ /*
+ * Bring CR0 into well-defined state. If it doesn't match with VMX
+ * requirements, vmxon will fail.
+ */
+ write_cr0(X86_CR0_HOST_STATE);
+
write_cr4(cr4 | X86_CR4_VMXE);
// TODO: validate CR4
unsigned long linux_ip = vmcs_read64(GUEST_RIP);
struct per_cpu *cpu_data = this_cpu_data();
+ cpu_data->linux_cr0 = vmcs_read64(GUEST_CR0);
cpu_data->linux_cr3 = vmcs_read64(GUEST_CR3);
cpu_data->linux_gdtr.base = vmcs_read64(GUEST_GDTR_BASE);