Complete Intel VMX Caretaker detach-time serialization, vCPU sync,
and ops registration in arch/x86/kvm/vmx/caretaker.c.

Signed-off-by: Pasha Tatashin <[email protected]>
---
 arch/x86/kvm/Makefile        |   9 +
 arch/x86/kvm/vmx/caretaker.c | 322 ++++++++++++++++++++++++++++++++++-
 arch/x86/kvm/vmx/vmx.c       |   4 +
 3 files changed, 333 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile
index 11e67c072258..b550a3fd0708 100644
--- a/arch/x86/kvm/Makefile
+++ b/arch/x86/kvm/Makefile
@@ -18,6 +18,8 @@ kvm-$(CONFIG_KVM_SMM) += smm.o
 
 kvm-intel-y            += vmx/vmx.o vmx/vmenter.o vmx/pmu_intel.o vmx/vmcs12.o 
\
                           vmx/nested.o vmx/posted_intr.o vmx/main.o
+kvm-intel-$(CONFIG_KVM_CARETAKER) += vmx/caretaker.o vmx/caretaker_vmenter.o
+
 
 kvm-intel-$(CONFIG_X86_SGX_KVM)        += vmx/sgx.o
 kvm-intel-$(CONFIG_KVM_HYPERV) += vmx/hyperv.o vmx/hyperv_evmcs.o
@@ -44,6 +46,13 @@ $(obj)/svm/vmenter.o: $(obj)/kvm-asm-offsets.h
 AFLAGS_vmx/vmenter.o    := -iquote $(obj)
 $(obj)/vmx/vmenter.o: $(obj)/kvm-asm-offsets.h
 
+AFLAGS_vmx/caretaker_vmenter.o := -iquote $(obj)
+$(obj)/vmx/caretaker_vmenter.o: $(obj)/kvm-asm-offsets.h
+
+CFLAGS_REMOVE_vmx/caretaker.o = $(CC_FLAGS_FTRACE)
+CFLAGS_vmx/caretaker.o := -fno-stack-protector
+
+
 $(obj)/kvm-asm-offsets.h: $(obj)/kvm-asm-offsets.s FORCE
        $(call filechk,offsets,__KVM_ASM_OFFSETS_H__)
 
diff --git a/arch/x86/kvm/vmx/caretaker.c b/arch/x86/kvm/vmx/caretaker.c
index 47b4743650e5..efef1c30c0d1 100644
--- a/arch/x86/kvm/vmx/caretaker.c
+++ b/arch/x86/kvm/vmx/caretaker.c
@@ -49,8 +49,8 @@ static int vmx_caretaker_init_page(struct caretaker_vmx_page 
*cvp,
        u64 basic_msr, misc_msr;
        int ret;
 
-       if (!vmx->vmcs01.vmcs)
-               return -EINVAL;
+       if (!vmx->vmcs01.vmcs || is_guest_mode(vcpu))
+               return -EOPNOTSUPP;
 
        ret = kvm_x86_caretaker_init_common_page(&cvp->common, vcpu, 
sizeof(*cvp));
        if (ret)
@@ -336,5 +336,323 @@ vmx_caretaker_init_host_vmcs(struct caretaker_vmx_page 
*cvp)
                vmx_vmwrite(PLE_WINDOW, 4096);
        }
        vmx_vmwrite(CPU_BASED_VM_EXEC_CONTROL, cpu_ctl);
+
+       /*
+        * Disable hardware MSR autoload lists while running in Caretaker so
+        * VM-entry/VM-exit do not dereference unpreserved host 
vmx->msr_autoload
+        * memory after kexec.
+        */
+       vmx_vmwrite(VM_EXIT_MSR_LOAD_COUNT, 0);
+       vmx_vmwrite(VM_EXIT_MSR_STORE_COUNT, 0);
+       vmx_vmwrite(VM_ENTRY_MSR_LOAD_COUNT, 0);
+}
+
+/*
+ * Guest-visible VMCS fields carried from the VMCS the caretaker ran the vCPU
+ * on to the VMCS the new kernel allocated for it.
+ *
+ * The new kernel does not adopt the old VMCS: it belongs to the previous
+ * kernel's struct loaded_vmcs, whose layout is not part of any handover ABI,
+ * and the VMCS region itself is opaque and implementation defined.  So the
+ * architecturally defined guest state is copied field by field instead.
+ *
+ * GUEST_IA32_EFER is handled separately because it is only written back when
+ * the caretaker actually recorded a value for it.
+ */
+static const u16 vmx_caretaker_guest_fields[] = {
+       GUEST_CS_SELECTOR,      GUEST_CS_LIMIT,
+       GUEST_CS_AR_BYTES,      GUEST_CS_BASE,
+       GUEST_SS_SELECTOR,      GUEST_SS_LIMIT,
+       GUEST_SS_AR_BYTES,      GUEST_SS_BASE,
+       GUEST_DS_SELECTOR,      GUEST_DS_LIMIT,
+       GUEST_DS_AR_BYTES,      GUEST_DS_BASE,
+       GUEST_ES_SELECTOR,      GUEST_ES_LIMIT,
+       GUEST_ES_AR_BYTES,      GUEST_ES_BASE,
+       GUEST_FS_SELECTOR,      GUEST_FS_LIMIT,
+       GUEST_FS_AR_BYTES,      GUEST_FS_BASE,
+       GUEST_GS_SELECTOR,      GUEST_GS_LIMIT,
+       GUEST_GS_AR_BYTES,      GUEST_GS_BASE,
+       GUEST_TR_SELECTOR,      GUEST_TR_LIMIT,
+       GUEST_TR_AR_BYTES,      GUEST_TR_BASE,
+       GUEST_LDTR_SELECTOR,    GUEST_LDTR_LIMIT,
+       GUEST_LDTR_AR_BYTES,    GUEST_LDTR_BASE,
+       GUEST_GDTR_LIMIT,       GUEST_GDTR_BASE,
+       GUEST_IDTR_LIMIT,       GUEST_IDTR_BASE,
+       GUEST_INTERRUPTIBILITY_INFO,
+       GUEST_ACTIVITY_STATE,
+       GUEST_IA32_DEBUGCTL,
+       GUEST_SYSENTER_CS,
+       GUEST_SYSENTER_ESP,
+       GUEST_SYSENTER_EIP,
+};
+
+static void
+vmx_caretaker_sync_vcpu(struct kvm_vcpu *vcpu, void *vcpu_data)
+{
+       struct kvm_caretaker_arch_ser *abi = vcpu_data;
+       struct vcpu_vmx *vmx = to_vmx(vcpu);
+       phys_addr_t cur_vmcs_pa = vmx->loaded_vmcs ? 
virt_to_phys(vmx->loaded_vmcs->vmcs) : 0;
+       struct vmcs *prev_vmcs;
+
+       guard(preempt)();
+       prev_vmcs = this_cpu_read(current_vmcs);
+
+       if (abi->vmcs_pa && cur_vmcs_pa && abi->vmcs_pa != cur_vmcs_pa) {
+               /* 42 * 8 bytes; this runs on the host stack, not a preserved 
one. */
+               unsigned long val[ARRAY_SIZE(vmx_caretaker_guest_fields)];
+               unsigned long guest_efer;
+               int i;
+
+               asm volatile("vmptrld %0" : : "m" (abi->vmcs_pa) : "memory", 
"cc");
+
+               for (i = 0; i < ARRAY_SIZE(vmx_caretaker_guest_fields); i++)
+                       val[i] = vmx_vmread(vmx_caretaker_guest_fields[i]);
+               guest_efer = vmx_caretaker_read_efer();
+
+               asm volatile("vmclear %0" : : "m" (abi->vmcs_pa) : "memory", 
"cc");
+               asm volatile("vmptrld %0" : : "m" (cur_vmcs_pa) : "memory", 
"cc");
+
+               for (i = 0; i < ARRAY_SIZE(vmx_caretaker_guest_fields); i++)
+                       vmx_vmwrite(vmx_caretaker_guest_fields[i], val[i]);
+               if (guest_efer)
+                       vmx_vmwrite(GUEST_IA32_EFER, guest_efer);
+
+               abi->vmcs_pa = cur_vmcs_pa;
+       } else if (cur_vmcs_pa) {
+               asm volatile("vmptrld %0" : : "m" (cur_vmcs_pa) : "memory", 
"cc");
+       }
+
+       kvm_x86_caretaker_sync_vcpu_common(vcpu);
+
+       if (vmx->loaded_vmcs) {
+               pin_controls_clearbit(vmx, PIN_BASED_VMX_PREEMPTION_TIMER);
+               vmcs_write32(PIN_BASED_VM_EXEC_CONTROL, pin_controls_get(vmx));
+               vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, exec_controls_get(vmx));
+               vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, 0);
+               vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, vmx->msr_autoload.host.nr);
+               vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, 
vmx->msr_autoload.guest.nr);
+               memset(&vmx->loaded_vmcs->host_state, 0,
+                      sizeof(struct vmcs_host_state));
+               list_del_init(&vmx->loaded_vmcs->loaded_vmcss_on_cpu_link);
+               vmx->loaded_vmcs->cpu = -1;
+               vmx->loaded_vmcs->launched = 0;
+       }
+       vmx_segment_cache_clear(vmx);
+       vmx->vt.guest_state_loaded = false;
+       vmx->guest_uret_msrs_loaded = false;
+
+       vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, 0);
+       vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, 0);
+       vmcs_write32(GUEST_ACTIVITY_STATE, GUEST_ACTIVITY_ACTIVE);
+       vmcs_writel(GUEST_PENDING_DBG_EXCEPTIONS, 0);
+
+       vmcs_writel(GUEST_RIP, kvm_rip_read(vcpu));
+       vmcs_writel(GUEST_RSP, kvm_rsp_read(vcpu));
+       vmcs_writel(GUEST_RFLAGS, kvm_get_rflags(vcpu));
+       vmx_set_cr0(vcpu, vcpu->arch.cr0);
+       vmcs_writel(GUEST_CR3, vcpu->arch.cr3);
+       vmx_set_cr4(vcpu, vcpu->arch.cr4);
+       vmx_set_efer(vcpu, vcpu->arch.efer);
+
+       if (vmx->loaded_vmcs)
+               vmx_set_constant_host_state(vmx);
+
+       if (cur_vmcs_pa)
+               asm volatile("vmclear %0" : : "m" (cur_vmcs_pa) : "memory", 
"cc");
+
+       if (prev_vmcs && (!vmx->loaded_vmcs || prev_vmcs != 
vmx->loaded_vmcs->vmcs)) {
+               vmcs_load(prev_vmcs);
+               this_cpu_write(current_vmcs, prev_vmcs);
+       } else {
+               this_cpu_write(current_vmcs, NULL);
+       }
+}
+
+static __cpu_preserved_text void
+vmx_caretaker_detach_serialize(void *page, struct kvm_vcpu_arch_ser *state)
+{
+       struct caretaker_vmx_page *cvp = page;
+
+       kvm_x86_caretaker_detach_serialize_common(&cvp->common, state);
+       kvm_x86_caretaker_update_msr(state, MSR_STAR, cvp->star);
+       kvm_x86_caretaker_update_msr(state, MSR_LSTAR, cvp->lstar);
+       kvm_x86_caretaker_update_msr(state, MSR_SYSCALL_MASK, cvp->fmask);
+       kvm_x86_caretaker_update_msr(state, MSR_KERNEL_GS_BASE,
+                                    cvp->common.kernel_gs_base);
+}
+
+static __cpu_preserved_text void vmx_caretaker_arm_timer(void *page, u64 
deadline_ticks)
+{
+       struct caretaker_vmx_page *cvp = page;
+       u32 shift = (cvp && cvp->timer_shift) ? cvp->timer_shift : 
VMX_PREEMPTION_TIMER_SHIFT;
+       u32 timer_value = 0;
+       u32 pin;
+
+       if (deadline_ticks) {
+               u64 now = arch_oncore_read_counter();
+
+               if (deadline_ticks > now) {
+                       u64 remaining = deadline_ticks - now;
+
+                       timer_value = (u32)(remaining >> shift);
+                       if (timer_value == 0)
+                               timer_value = 1;
+               } else {
+                       timer_value = 1;
+               }
+       }
+
+       if (timer_value > 0) {
+               vmx_vmwrite(VMX_PREEMPTION_TIMER_VALUE, timer_value);
+               pin = (u32)vmx_vmread(PIN_BASED_VM_EXEC_CONTROL);
+               pin |= PIN_BASED_VMX_PREEMPTION_TIMER;
+               vmx_vmwrite(PIN_BASED_VM_EXEC_CONTROL, pin);
+       } else {
+               vmx_caretaker_disarm_timer(page);
+       }
 }
 
+static __cpu_preserved_text void
+vmx_caretaker_advance_rip(void *page, u64 rip)
+{
+       struct caretaker_vmx_page *cvp = page;
+
+       cvp->common.last_exit_rip = rip;
+       vmx_vmwrite(GUEST_RIP, rip);
+}
+
+static __cpu_preserved_text void vmx_caretaker_pre_enter(void *page)
+{
+       struct caretaker_vmx_page *cvp = page;
+
+       /* Ensure VMX is active on this core */
+       if (!(__read_cr4() & X86_CR4_VMXE)) {
+               asm volatile("mov %0, %%cr4" : : "r" (__read_cr4() | 
X86_CR4_VMXE) : "memory");
+               if (cvp->vmxon_pa) {
+                       asm volatile("1: vmxon %[vmxon_pa]\n\t"
+                                    "2:\n\t"
+                                    _ASM_EXTABLE(1b, 2b)
+                                    : : [vmxon_pa] "m" (cvp->vmxon_pa)
+                                    : "memory", "cc");
+               }
+       }
+
+       /* Activate VMCS on this pCPU */
+       asm volatile("vmptrld %0" : : "m" (cvp->common.abi.vmcs_pa) : "memory", 
"cc");
+
+       /* Configure Caretaker host VMCS */
+       vmx_caretaker_init_host_vmcs(cvp);
+
+       if (cvp->common.arch_state)
+               native_write_cr2(cvp->common.arch_state->sregs.cr2);
+
+       native_wrmsrq(MSR_STAR, cvp->star);
+       native_wrmsrq(MSR_LSTAR, cvp->lstar);
+       native_wrmsrq(MSR_SYSCALL_MASK, cvp->fmask);
+}
+
+static __cpu_preserved_text void
+vmx_caretaker_read_seg(struct kvm_segment *var, u16 sel_field,
+                      u16 base_field, u16 limit_field, u16 ar_field)
+{
+       u32 ar = (u32)vmx_vmread(ar_field);
+
+       var->base = vmx_vmread(base_field);
+       var->limit = (u32)vmx_vmread(limit_field);
+       var->selector = (u16)vmx_vmread(sel_field);
+       var->unusable = (ar >> 16) & 1;
+       var->type = ar & 15;
+       var->s = (ar >> 4) & 1;
+       var->dpl = (ar >> 5) & 3;
+       var->present = !var->unusable;
+       var->avl = (ar >> 12) & 1;
+       var->l = (ar >> 13) & 1;
+       var->db = (ar >> 14) & 1;
+       var->g = (ar >> 15) & 1;
+}
+
+static __cpu_preserved_text void vmx_caretaker_post_exit(void *page)
+{
+       struct caretaker_vmx_page *cvp = page;
+       struct kvm_vcpu_arch_ser *state = cvp->common.arch_state;
+       u64 efer;
+
+       cvp->star = native_rdmsrq(MSR_STAR);
+       cvp->lstar = native_rdmsrq(MSR_LSTAR);
+       cvp->fmask = native_rdmsrq(MSR_SYSCALL_MASK);
+
+       cvp->common.cr0 = vmx_caretaker_read_cr0();
+       cvp->common.cr3 = vmx_vmread(GUEST_CR3);
+       cvp->common.cr4 = vmx_caretaker_read_cr4();
+       efer = vmx_caretaker_read_efer();
+       if (efer)
+               cvp->common.efer = efer;
+       cvp->common.last_exit_rip = vmx_vmread(GUEST_RIP);
+       cvp->common.last_exit_rsp = vmx_vmread(GUEST_RSP);
+       cvp->common.last_exit_rflags = vmx_vmread(GUEST_RFLAGS);
+
+       if (state) {
+               state->sregs.cr2 = native_read_cr2();
+               vmx_caretaker_read_seg(&state->sregs.cs, GUEST_CS_SELECTOR,
+                                      GUEST_CS_BASE, GUEST_CS_LIMIT, 
GUEST_CS_AR_BYTES);
+               vmx_caretaker_read_seg(&state->sregs.ds, GUEST_DS_SELECTOR,
+                                      GUEST_DS_BASE, GUEST_DS_LIMIT, 
GUEST_DS_AR_BYTES);
+               vmx_caretaker_read_seg(&state->sregs.es, GUEST_ES_SELECTOR,
+                                      GUEST_ES_BASE, GUEST_ES_LIMIT, 
GUEST_ES_AR_BYTES);
+               vmx_caretaker_read_seg(&state->sregs.fs, GUEST_FS_SELECTOR,
+                                      GUEST_FS_BASE, GUEST_FS_LIMIT, 
GUEST_FS_AR_BYTES);
+               vmx_caretaker_read_seg(&state->sregs.gs, GUEST_GS_SELECTOR,
+                                      GUEST_GS_BASE, GUEST_GS_LIMIT, 
GUEST_GS_AR_BYTES);
+               vmx_caretaker_read_seg(&state->sregs.ss, GUEST_SS_SELECTOR,
+                                      GUEST_SS_BASE, GUEST_SS_LIMIT, 
GUEST_SS_AR_BYTES);
+               vmx_caretaker_read_seg(&state->sregs.tr, GUEST_TR_SELECTOR,
+                                      GUEST_TR_BASE, GUEST_TR_LIMIT, 
GUEST_TR_AR_BYTES);
+               vmx_caretaker_read_seg(&state->sregs.ldt, GUEST_LDTR_SELECTOR,
+                                      GUEST_LDTR_BASE, GUEST_LDTR_LIMIT, 
GUEST_LDTR_AR_BYTES);
+               state->sregs.gdt.base = vmx_vmread(GUEST_GDTR_BASE);
+               state->sregs.gdt.limit = (u16)vmx_vmread(GUEST_GDTR_LIMIT);
+               state->sregs.idt.base = vmx_vmread(GUEST_IDTR_BASE);
+               state->sregs.idt.limit = (u16)vmx_vmread(GUEST_IDTR_LIMIT);
+
+               kvm_x86_caretaker_update_msr(state, MSR_IA32_SYSENTER_CS,
+                                            vmx_vmread(GUEST_SYSENTER_CS));
+               kvm_x86_caretaker_update_msr(state, MSR_IA32_SYSENTER_ESP,
+                                            vmx_vmread(GUEST_SYSENTER_ESP));
+               kvm_x86_caretaker_update_msr(state, MSR_IA32_SYSENTER_EIP,
+                                            vmx_vmread(GUEST_SYSENTER_EIP));
+       }
+
+       /* Flush VMCS cache so host and incoming kernel see latest guest state 
*/
+       asm volatile("vmclear %0" : : "m" (cvp->common.abi.vmcs_pa) : "memory", 
"cc");
+}
+
+static const struct kvm_x86_caretaker_runtime_ops vmx_caretaker_runtime_ops 
__cpu_preserved_data = {
+       .detach_serialize = vmx_caretaker_detach_serialize,
+       .common = {
+               .enter_guest = vmx_caretaker_enter,
+               .decode_exit = vmx_caretaker_decode_exit,
+               .handle_arch_exit = kvm_x86_caretaker_handle_exit,
+               .advance_rip = vmx_caretaker_advance_rip,
+               .arm_timer = vmx_caretaker_arm_timer,
+               .disarm_timer = vmx_caretaker_disarm_timer,
+               .pre_run = vmx_caretaker_pre_enter,
+               .post_run = vmx_caretaker_post_exit,
+       },
+};
+
+static const struct kvm_x86_caretaker_ops vmx_caretaker_ops = {
+       .name = "vmx",
+       .init = vmx_caretaker_init,
+       .sync_vcpu = vmx_caretaker_sync_vcpu,
+       .runtime = &vmx_caretaker_runtime_ops,
+};
+
+void vmx_caretaker_register(void)
+{
+       kvm_x86_caretaker_register_ops(&vmx_caretaker_ops);
+}
+
+void vmx_caretaker_unregister(void)
+{
+       kvm_x86_caretaker_unregister_ops(&vmx_caretaker_ops);
+}
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 48b6e69f99bf..817623f7429a 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -54,6 +54,7 @@
 #include <trace/events/ipi.h>
 
 #include "capabilities.h"
+#include "caretaker.h"
 #include "common.h"
 #include "cpuid.h"
 #include "hyperv.h"
@@ -8548,6 +8549,7 @@ void vmx_migrate_timers(struct kvm_vcpu *vcpu)
 
 void vmx_hardware_unsetup(void)
 {
+       vmx_caretaker_unregister();
        kvm_set_posted_intr_wakeup_handler(NULL);
 
        if (nested)
@@ -8855,6 +8857,8 @@ __init int vmx_hardware_setup(void)
 
        kvm_caps.inapplicable_quirks &= ~KVM_X86_QUIRK_IGNORE_GUEST_PAT;
 
+       vmx_caretaker_register();
+
        return 0;
 }
 
-- 
2.55.0.1082.g2b9226bbc0-goog


Reply via email to