aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2014-05-14 03:39:49 -0400
committerPaolo Bonzini <pbonzini@redhat.com>2014-05-22 11:47:17 -0400
commitae9fedc793c4d98aa9bb298585b2b9246096ce65 (patch)
treeac40c938ab01e7c943a21d67eeaba5069db54632
parent5045b468037dfe1c848827ce10e99d87f5669160 (diff)
KVM: x86: get CPL from SS.DPL
CS.RPL is not equal to the CPL in the few instructions between setting CR0.PE and reloading CS. And CS.DPL is also not equal to the CPL for conforming code segments. However, SS.DPL *is* always equal to the CPL except for the weird case of SYSRET on AMD processors, which sets SS.DPL=SS.RPL from the value in the STAR MSR, but force CPL=3 (Intel instead forces SS.DPL=SS.RPL=CPL=3). So this patch: - modifies SVM to update the CPL from SS.DPL rather than CS.RPL; the above case with SYSRET is not broken further, and the way to fix it would be to pass the CPL to userspace and back - modifies VMX to always return the CPL from SS.DPL (except forcing it to 0 if we are emulating real mode via vm86 mode; in vm86 mode all DPLs have to be 3, but real mode does allow privileged instructions). It also removes the CPL cache, which becomes a duplicate of the SS access rights cache. This fixes doing KVM_IOCTL_SET_SREGS exactly after setting CR0.PE=1 but before CS has been reloaded. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r--arch/x86/include/asm/kvm_host.h1
-rw-r--r--arch/x86/kvm/svm.c35
-rw-r--r--arch/x86/kvm/vmx.c24
3 files changed, 18 insertions, 42 deletions
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index e21aee98a5c2..49314155b66c 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -130,7 +130,6 @@ enum kvm_reg_ex {
130 VCPU_EXREG_PDPTR = NR_VCPU_REGS, 130 VCPU_EXREG_PDPTR = NR_VCPU_REGS,
131 VCPU_EXREG_CR3, 131 VCPU_EXREG_CR3,
132 VCPU_EXREG_RFLAGS, 132 VCPU_EXREG_RFLAGS,
133 VCPU_EXREG_CPL,
134 VCPU_EXREG_SEGMENTS, 133 VCPU_EXREG_SEGMENTS,
135}; 134};
136 135
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 0b7d58d0c5fb..ec8366c5cfea 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -1338,21 +1338,6 @@ static void svm_vcpu_put(struct kvm_vcpu *vcpu)
1338 wrmsrl(host_save_user_msrs[i], svm->host_user_msrs[i]); 1338 wrmsrl(host_save_user_msrs[i], svm->host_user_msrs[i]);
1339} 1339}
1340 1340
1341static void svm_update_cpl(struct kvm_vcpu *vcpu)
1342{
1343 struct vcpu_svm *svm = to_svm(vcpu);
1344 int cpl;
1345
1346 if (!is_protmode(vcpu))
1347 cpl = 0;
1348 else if (svm->vmcb->save.rflags & X86_EFLAGS_VM)
1349 cpl = 3;
1350 else
1351 cpl = svm->vmcb->save.cs.selector & 0x3;
1352
1353 svm->vmcb->save.cpl = cpl;
1354}
1355
1356static unsigned long svm_get_rflags(struct kvm_vcpu *vcpu) 1341static unsigned long svm_get_rflags(struct kvm_vcpu *vcpu)
1357{ 1342{
1358 return to_svm(vcpu)->vmcb->save.rflags; 1343 return to_svm(vcpu)->vmcb->save.rflags;
@@ -1360,11 +1345,12 @@ static unsigned long svm_get_rflags(struct kvm_vcpu *vcpu)
1360 1345
1361static void svm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags) 1346static void svm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags)
1362{ 1347{
1363 unsigned long old_rflags = to_svm(vcpu)->vmcb->save.rflags; 1348 /*
1364 1349 * Any change of EFLAGS.VM is accompained by a reload of SS
1350 * (caused by either a task switch or an inter-privilege IRET),
1351 * so we do not need to update the CPL here.
1352 */
1365 to_svm(vcpu)->vmcb->save.rflags = rflags; 1353 to_svm(vcpu)->vmcb->save.rflags = rflags;
1366 if ((old_rflags ^ rflags) & X86_EFLAGS_VM)
1367 svm_update_cpl(vcpu);
1368} 1354}
1369 1355
1370static void svm_cache_reg(struct kvm_vcpu *vcpu, enum kvm_reg reg) 1356static void svm_cache_reg(struct kvm_vcpu *vcpu, enum kvm_reg reg)
@@ -1631,8 +1617,15 @@ static void svm_set_segment(struct kvm_vcpu *vcpu,
1631 s->attrib |= (var->db & 1) << SVM_SELECTOR_DB_SHIFT; 1617 s->attrib |= (var->db & 1) << SVM_SELECTOR_DB_SHIFT;
1632 s->attrib |= (var->g & 1) << SVM_SELECTOR_G_SHIFT; 1618 s->attrib |= (var->g & 1) << SVM_SELECTOR_G_SHIFT;
1633 } 1619 }
1634 if (seg == VCPU_SREG_CS) 1620
1635 svm_update_cpl(vcpu); 1621 /*
1622 * This is always accurate, except if SYSRET returned to a segment
1623 * with SS.DPL != 3. Intel does not have this quirk, and always
1624 * forces SS.DPL to 3 on sysret, so we ignore that case; fixing it
1625 * would entail passing the CPL to userspace and back.
1626 */
1627 if (seg == VCPU_SREG_SS)
1628 svm->vmcb->save.cpl = (s->attrib >> SVM_SELECTOR_DPL_SHIFT) & 3;
1636 1629
1637 mark_dirty(svm->vmcb, VMCB_SEG); 1630 mark_dirty(svm->vmcb, VMCB_SEG);
1638} 1631}
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 6f7463f53ed9..a267108403f5 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -414,7 +414,6 @@ struct vcpu_vmx {
414 struct kvm_vcpu vcpu; 414 struct kvm_vcpu vcpu;
415 unsigned long host_rsp; 415 unsigned long host_rsp;
416 u8 fail; 416 u8 fail;
417 u8 cpl;
418 bool nmi_known_unmasked; 417 bool nmi_known_unmasked;
419 u32 exit_intr_info; 418 u32 exit_intr_info;
420 u32 idt_vectoring_info; 419 u32 idt_vectoring_info;
@@ -3150,10 +3149,6 @@ static void enter_pmode(struct kvm_vcpu *vcpu)
3150 fix_pmode_seg(vcpu, VCPU_SREG_DS, &vmx->rmode.segs[VCPU_SREG_DS]); 3149 fix_pmode_seg(vcpu, VCPU_SREG_DS, &vmx->rmode.segs[VCPU_SREG_DS]);
3151 fix_pmode_seg(vcpu, VCPU_SREG_FS, &vmx->rmode.segs[VCPU_SREG_FS]); 3150 fix_pmode_seg(vcpu, VCPU_SREG_FS, &vmx->rmode.segs[VCPU_SREG_FS]);
3152 fix_pmode_seg(vcpu, VCPU_SREG_GS, &vmx->rmode.segs[VCPU_SREG_GS]); 3151 fix_pmode_seg(vcpu, VCPU_SREG_GS, &vmx->rmode.segs[VCPU_SREG_GS]);
3153
3154 /* CPL is always 0 when CPU enters protected mode */
3155 __set_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail);
3156 vmx->cpl = 0;
3157} 3152}
3158 3153
3159static void fix_rmode_seg(int seg, struct kvm_segment *save) 3154static void fix_rmode_seg(int seg, struct kvm_segment *save)
@@ -3555,22 +3550,14 @@ static int vmx_get_cpl(struct kvm_vcpu *vcpu)
3555{ 3550{
3556 struct vcpu_vmx *vmx = to_vmx(vcpu); 3551 struct vcpu_vmx *vmx = to_vmx(vcpu);
3557 3552
3558 if (!is_protmode(vcpu)) 3553 if (unlikely(vmx->rmode.vm86_active))
3559 return 0; 3554 return 0;
3560 3555 else {
3561 if (!is_long_mode(vcpu) 3556 int ar = vmx_read_guest_seg_ar(vmx, VCPU_SREG_SS);
3562 && (kvm_get_rflags(vcpu) & X86_EFLAGS_VM)) /* if virtual 8086 */ 3557 return AR_DPL(ar);
3563 return 3;
3564
3565 if (!test_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail)) {
3566 __set_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail);
3567 vmx->cpl = vmx_read_guest_seg_selector(vmx, VCPU_SREG_CS) & 3;
3568 } 3558 }
3569
3570 return vmx->cpl;
3571} 3559}
3572 3560
3573
3574static u32 vmx_segment_access_rights(struct kvm_segment *var) 3561static u32 vmx_segment_access_rights(struct kvm_segment *var)
3575{ 3562{
3576 u32 ar; 3563 u32 ar;
@@ -3598,8 +3585,6 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu,
3598 const struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg]; 3585 const struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
3599 3586
3600 vmx_segment_cache_clear(vmx); 3587 vmx_segment_cache_clear(vmx);
3601 if (seg == VCPU_SREG_CS)
3602 __clear_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail);
3603 3588
3604 if (vmx->rmode.vm86_active && seg != VCPU_SREG_LDTR) { 3589 if (vmx->rmode.vm86_active && seg != VCPU_SREG_LDTR) {
3605 vmx->rmode.segs[seg] = *var; 3590 vmx->rmode.segs[seg] = *var;
@@ -7471,7 +7456,6 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)
7471 7456
7472 vcpu->arch.regs_avail = ~((1 << VCPU_REGS_RIP) | (1 << VCPU_REGS_RSP) 7457 vcpu->arch.regs_avail = ~((1 << VCPU_REGS_RIP) | (1 << VCPU_REGS_RSP)
7473 | (1 << VCPU_EXREG_RFLAGS) 7458 | (1 << VCPU_EXREG_RFLAGS)
7474 | (1 << VCPU_EXREG_CPL)
7475 | (1 << VCPU_EXREG_PDPTR) 7459 | (1 << VCPU_EXREG_PDPTR)
7476 | (1 << VCPU_EXREG_SEGMENTS) 7460 | (1 << VCPU_EXREG_SEGMENTS)
7477 | (1 << VCPU_EXREG_CR3)); 7461 | (1 << VCPU_EXREG_CR3));