OSDN Git Service

KVM: VMX: allow rmode_tss_base() to work with >2G of guest memory
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / drivers / kvm / vmx.c
index cc7ee3d..b400668 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include "kvm.h"
+#include "x86_emulate.h"
 #include "vmx.h"
 #include "segment_descriptor.h"
 
@@ -543,6 +544,7 @@ static void vmx_inject_gp(struct kvm_vcpu *vcpu, unsigned error_code)
 /*
  * Swap MSR entry in host/guest MSR entry array.
  */
+#ifdef CONFIG_X86_64
 static void move_msr_up(struct vcpu_vmx *vmx, int from, int to)
 {
        struct kvm_msr_entry tmp;
@@ -554,6 +556,7 @@ static void move_msr_up(struct vcpu_vmx *vmx, int from, int to)
        vmx->host_msrs[to] = vmx->host_msrs[from];
        vmx->host_msrs[from] = tmp;
 }
+#endif
 
 /*
  * Set up the vmcs to automatically save and restore system
@@ -839,13 +842,13 @@ static __init int adjust_vmx_controls(u32 ctl_min, u32 ctl_opt,
 
        /* Ensure minimum (required) set of control bits are supported. */
        if (ctl_min & ~ctl)
-               return -1;
+               return -EIO;
 
        *result = ctl;
        return 0;
 }
 
-static __init int setup_vmcs_config(void)
+static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)
 {
        u32 vmx_msr_low, vmx_msr_high;
        u32 min, opt;
@@ -858,7 +861,7 @@ static __init int setup_vmcs_config(void)
        opt = 0;
        if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_PINBASED_CTLS,
                                &_pin_based_exec_control) < 0)
-               return -1;
+               return -EIO;
 
        min = CPU_BASED_HLT_EXITING |
 #ifdef CONFIG_X86_64
@@ -871,7 +874,7 @@ static __init int setup_vmcs_config(void)
        opt = 0;
        if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_PROCBASED_CTLS,
                                &_cpu_based_exec_control) < 0)
-               return -1;
+               return -EIO;
 
        min = 0;
 #ifdef CONFIG_X86_64
@@ -880,37 +883,37 @@ static __init int setup_vmcs_config(void)
        opt = 0;
        if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_EXIT_CTLS,
                                &_vmexit_control) < 0)
-               return -1;
+               return -EIO;
 
        min = opt = 0;
        if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_ENTRY_CTLS,
                                &_vmentry_control) < 0)
-               return -1;
+               return -EIO;
 
        rdmsr(MSR_IA32_VMX_BASIC, vmx_msr_low, vmx_msr_high);
 
        /* IA-32 SDM Vol 3B: VMCS size is never greater than 4kB. */
        if ((vmx_msr_high & 0x1fff) > PAGE_SIZE)
-               return -1;
+               return -EIO;
 
 #ifdef CONFIG_X86_64
        /* IA-32 SDM Vol 3B: 64-bit CPUs always have VMX_BASIC_MSR[48]==0. */
        if (vmx_msr_high & (1u<<16))
-               return -1;
+               return -EIO;
 #endif
 
        /* Require Write-Back (WB) memory type for VMCS accesses. */
        if (((vmx_msr_high >> 18) & 15) != 6)
-               return -1;
+               return -EIO;
 
-       vmcs_config.size = vmx_msr_high & 0x1fff;
-       vmcs_config.order = get_order(vmcs_config.size);
-       vmcs_config.revision_id = vmx_msr_low;
+       vmcs_conf->size = vmx_msr_high & 0x1fff;
+       vmcs_conf->order = get_order(vmcs_config.size);
+       vmcs_conf->revision_id = vmx_msr_low;
 
-       vmcs_config.pin_based_exec_ctrl = _pin_based_exec_control;
-       vmcs_config.cpu_based_exec_ctrl = _cpu_based_exec_control;
-       vmcs_config.vmexit_ctrl         = _vmexit_control;
-       vmcs_config.vmentry_ctrl        = _vmentry_control;
+       vmcs_conf->pin_based_exec_ctrl = _pin_based_exec_control;
+       vmcs_conf->cpu_based_exec_ctrl = _cpu_based_exec_control;
+       vmcs_conf->vmexit_ctrl         = _vmexit_control;
+       vmcs_conf->vmentry_ctrl        = _vmentry_control;
 
        return 0;
 }
@@ -948,8 +951,6 @@ static void free_kvm_area(void)
                free_vmcs(per_cpu(vmxarea, cpu));
 }
 
-extern struct vmcs *alloc_vmcs_cpu(int cpu);
-
 static __init int alloc_kvm_area(void)
 {
        int cpu;
@@ -970,8 +971,8 @@ static __init int alloc_kvm_area(void)
 
 static __init int hardware_setup(void)
 {
-       if (setup_vmcs_config() < 0)
-               return -1;
+       if (setup_vmcs_config(&vmcs_config) < 0)
+               return -EIO;
        return alloc_kvm_area();
 }
 
@@ -1029,7 +1030,7 @@ static void enter_pmode(struct kvm_vcpu *vcpu)
        vmcs_write32(GUEST_CS_AR_BYTES, 0x9b);
 }
 
-static int rmode_tss_base(struct kvm* kvm)
+static gva_t rmode_tss_base(struct kvm* kvm)
 {
        gfn_t base_gfn = kvm->memslots[0].base_gfn + kvm->memslots[0].npages - 3;
        return base_gfn << PAGE_SHIFT;
@@ -1110,7 +1111,7 @@ static void enter_lmode(struct kvm_vcpu *vcpu)
        find_msr_entry(to_vmx(vcpu), MSR_EFER)->data |= EFER_LMA | EFER_LME;
        vmcs_write32(VM_ENTRY_CONTROLS,
                     vmcs_read32(VM_ENTRY_CONTROLS)
-                    | VM_ENTRY_CONTROLS_IA32E_MASK);
+                    | VM_ENTRY_IA32E_MODE);
 }
 
 static void exit_lmode(struct kvm_vcpu *vcpu)
@@ -1119,7 +1120,7 @@ static void exit_lmode(struct kvm_vcpu *vcpu)
 
        vmcs_write32(VM_ENTRY_CONTROLS,
                     vmcs_read32(VM_ENTRY_CONTROLS)
-                    & ~VM_ENTRY_CONTROLS_IA32E_MASK);
+                    & ~VM_ENTRY_IA32E_MODE);
 }
 
 #endif
@@ -1184,13 +1185,13 @@ static void vmx_set_efer(struct kvm_vcpu *vcpu, u64 efer)
        if (efer & EFER_LMA) {
                vmcs_write32(VM_ENTRY_CONTROLS,
                                     vmcs_read32(VM_ENTRY_CONTROLS) |
-                                    VM_ENTRY_CONTROLS_IA32E_MASK);
+                                    VM_ENTRY_IA32E_MODE);
                msr->data = efer;
 
        } else {
                vmcs_write32(VM_ENTRY_CONTROLS,
                                     vmcs_read32(VM_ENTRY_CONTROLS) &
-                                    ~VM_ENTRY_CONTROLS_IA32E_MASK);
+                                    ~VM_ENTRY_IA32E_MODE);
 
                msr->data = efer & ~EFER_LME;
        }
@@ -1553,8 +1554,8 @@ static void inject_rmode_irq(struct kvm_vcpu *vcpu, int irq)
                return;
        }
 
-       if (kvm_read_guest(vcpu, irq * sizeof(ent), sizeof(ent), &ent) !=
-                                                               sizeof(ent)) {
+       if (emulator_read_std(irq * sizeof(ent), &ent, sizeof(ent), vcpu) !=
+                                                       X86EMUL_CONTINUE) {
                vcpu_printf(vcpu, "%s: read guest err\n", __FUNCTION__);
                return;
        }
@@ -1564,9 +1565,9 @@ static void inject_rmode_irq(struct kvm_vcpu *vcpu, int irq)
        ip =  vmcs_readl(GUEST_RIP);
 
 
-       if (kvm_write_guest(vcpu, ss_base + sp - 2, 2, &flags) != 2 ||
-           kvm_write_guest(vcpu, ss_base + sp - 4, 2, &cs) != 2 ||
-           kvm_write_guest(vcpu, ss_base + sp - 6, 2, &ip) != 2) {
+       if (emulator_write_emulated(ss_base + sp - 2, &flags, 2, vcpu) != X86EMUL_CONTINUE ||
+           emulator_write_emulated(ss_base + sp - 4, &cs, 2, vcpu) != X86EMUL_CONTINUE ||
+           emulator_write_emulated(ss_base + sp - 6, &ip, 2, vcpu) != X86EMUL_CONTINUE) {
                vcpu_printf(vcpu, "%s: write guest err\n", __FUNCTION__);
                return;
        }
@@ -1762,80 +1763,29 @@ static int handle_triple_fault(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
        return 0;
 }
 
-static int get_io_count(struct kvm_vcpu *vcpu, unsigned long *count)
-{
-       u64 inst;
-       gva_t rip;
-       int countr_size;
-       int i, n;
-
-       if ((vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_VM)) {
-               countr_size = 2;
-       } else {
-               u32 cs_ar = vmcs_read32(GUEST_CS_AR_BYTES);
-
-               countr_size = (cs_ar & AR_L_MASK) ? 8:
-                             (cs_ar & AR_DB_MASK) ? 4: 2;
-       }
-
-       rip =  vmcs_readl(GUEST_RIP);
-       if (countr_size != 8)
-               rip += vmcs_readl(GUEST_CS_BASE);
-
-       n = kvm_read_guest(vcpu, rip, sizeof(inst), &inst);
-
-       for (i = 0; i < n; i++) {
-               switch (((u8*)&inst)[i]) {
-               case 0xf0:
-               case 0xf2:
-               case 0xf3:
-               case 0x2e:
-               case 0x36:
-               case 0x3e:
-               case 0x26:
-               case 0x64:
-               case 0x65:
-               case 0x66:
-                       break;
-               case 0x67:
-                       countr_size = (countr_size == 2) ? 4: (countr_size >> 1);
-               default:
-                       goto done;
-               }
-       }
-       return 0;
-done:
-       countr_size *= 8;
-       *count = vcpu->regs[VCPU_REGS_RCX] & (~0ULL >> (64 - countr_size));
-       //printk("cx: %lx\n", vcpu->regs[VCPU_REGS_RCX]);
-       return 1;
-}
-
 static int handle_io(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
 {
        u64 exit_qualification;
        int size, down, in, string, rep;
        unsigned port;
-       unsigned long count;
-       gva_t address;
 
        ++vcpu->stat.io_exits;
        exit_qualification = vmcs_read64(EXIT_QUALIFICATION);
-       in = (exit_qualification & 8) != 0;
-       size = (exit_qualification & 7) + 1;
        string = (exit_qualification & 16) != 0;
+
+       if (string) {
+               if (emulate_instruction(vcpu, kvm_run, 0, 0) == EMULATE_DO_MMIO)
+                       return 0;
+               return 1;
+       }
+
+       size = (exit_qualification & 7) + 1;
+       in = (exit_qualification & 8) != 0;
        down = (vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_DF) != 0;
-       count = 1;
        rep = (exit_qualification & 32) != 0;
        port = exit_qualification >> 16;
-       address = 0;
-       if (string) {
-               if (rep && !get_io_count(vcpu, &count))
-                       return 1;
-               address = vmcs_readl(GUEST_LINEAR_ADDRESS);
-       }
-       return kvm_setup_pio(vcpu, kvm_run, in, size, count, string, down,
-                            address, rep, port);
+
+       return kvm_emulate_pio(vcpu, kvm_run, in, size, port);
 }
 
 static void
@@ -1881,7 +1831,8 @@ static int handle_cr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
                        vcpu_load_rsp_rip(vcpu);
                        set_cr8(vcpu, vcpu->regs[reg]);
                        skip_emulated_instruction(vcpu);
-                       return 1;
+                       kvm_run->exit_reason = KVM_EXIT_SET_TPR;
+                       return 0;
                };
                break;
        case 2: /* clts */
@@ -1917,7 +1868,7 @@ static int handle_cr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
                break;
        }
        kvm_run->exit_reason = 0;
-       printk(KERN_ERR "kvm: unhandled control register: op %d cr %d\n",
+       pr_unimpl(vcpu, "unhandled control register: op %d cr %d\n",
               (int)(exit_qualification >> 4) & 3, cr);
        return 0;
 }
@@ -2116,9 +2067,6 @@ again:
 
        preempt_disable();
 
-       if (!vcpu->mmio_read_completed)
-               do_interrupt_requests(vcpu, kvm_run);
-
        vmx_save_host_state(vmx);
        kvm_load_guest_fpu(vcpu);
 
@@ -2129,6 +2077,18 @@ again:
 
        local_irq_disable();
 
+       if (signal_pending(current)) {
+               local_irq_enable();
+               preempt_enable();
+               r = -EINTR;
+               kvm_run->exit_reason = KVM_EXIT_INTR;
+               ++vcpu->stat.signal_exits;
+               goto out;
+       }
+
+       if (!vcpu->mmio_read_completed)
+               do_interrupt_requests(vcpu, kvm_run);
+
        vcpu->guest_mode = 1;
        if (vcpu->requests)
                if (test_and_clear_bit(KVM_TLB_FLUSH, &vcpu->requests))
@@ -2277,14 +2237,6 @@ again:
 
        r = kvm_handle_exit(kvm_run, vcpu);
        if (r > 0) {
-               /* Give scheduler a change to reschedule. */
-               if (signal_pending(current)) {
-                       r = -EINTR;
-                       kvm_run->exit_reason = KVM_EXIT_INTR;
-                       ++vcpu->stat.signal_exits;
-                       goto out;
-               }
-
                if (dm_request_for_irq_injection(vcpu, kvm_run)) {
                        r = -EINTR;
                        kvm_run->exit_reason = KVM_EXIT_INTR;
@@ -2356,13 +2308,13 @@ static void vmx_free_vcpu(struct kvm_vcpu *vcpu)
        kfree(vmx->host_msrs);
        kfree(vmx->guest_msrs);
        kvm_vcpu_uninit(vcpu);
-       kfree(vmx);
+       kmem_cache_free(kvm_vcpu_cache, vmx);
 }
 
 static struct kvm_vcpu *vmx_create_vcpu(struct kvm *kvm, unsigned int id)
 {
        int err;
-       struct vcpu_vmx *vmx = kzalloc(sizeof(*vmx), GFP_KERNEL);
+       struct vcpu_vmx *vmx = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL);
        int cpu;
 
        if (!vmx)
@@ -2407,15 +2359,30 @@ free_guest_msrs:
 uninit_vcpu:
        kvm_vcpu_uninit(&vmx->vcpu);
 free_vcpu:
-       kfree(vmx);
+       kmem_cache_free(kvm_vcpu_cache, vmx);
        return ERR_PTR(err);
 }
 
+static void __init vmx_check_processor_compat(void *rtn)
+{
+       struct vmcs_config vmcs_conf;
+
+       *(int *)rtn = 0;
+       if (setup_vmcs_config(&vmcs_conf) < 0)
+               *(int *)rtn = -EIO;
+       if (memcmp(&vmcs_config, &vmcs_conf, sizeof(struct vmcs_config)) != 0) {
+               printk(KERN_ERR "kvm: CPU %d feature inconsistency!\n",
+                               smp_processor_id());
+               *(int *)rtn = -EIO;
+       }
+}
+
 static struct kvm_arch_ops vmx_arch_ops = {
        .cpu_has_kvm_support = cpu_has_kvm_support,
        .disabled_by_bios = vmx_disabled_by_bios,
        .hardware_setup = hardware_setup,
        .hardware_unsetup = hardware_unsetup,
+       .check_processor_compatibility = vmx_check_processor_compat,
        .hardware_enable = hardware_enable,
        .hardware_disable = hardware_disable,
 
@@ -2487,7 +2454,7 @@ static int __init vmx_init(void)
        memset(iova, 0xff, PAGE_SIZE);
        kunmap(vmx_io_bitmap_b);
 
-       r = kvm_init_arch(&vmx_arch_ops, THIS_MODULE);
+       r = kvm_init_arch(&vmx_arch_ops, sizeof(struct vcpu_vmx), THIS_MODULE);
        if (r)
                goto out1;