aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kvm/mmu.c
diff options
context:
space:
mode:
authorAvi Kivity <avi@redhat.com>2012-09-12 07:52:00 -0400
committerAvi Kivity <avi@redhat.com>2012-09-20 06:00:08 -0400
commit97d64b788114be1c4dc4bfe7a8ba2bf9643fe6af (patch)
tree50dfed391cb52aba63cc41d0cdbdf07ee2d792e4 /arch/x86/kvm/mmu.c
parent8cbc70696f149e44753b0fe60162b4ff96c2dd2b (diff)
KVM: MMU: Optimize pte permission checks
walk_addr_generic() permission checks are a maze of branchy code, which is performed four times per lookup. It depends on the type of access, efer.nxe, cr0.wp, cr4.smep, and in the near future, cr4.smap. Optimize this away by precalculating all variants and storing them in a bitmap. The bitmap is recalculated when rarely-changing variables change (cr0, cr4) and is indexed by the often-changing variables (page fault error code, pte access permissions). The permission check is moved to the end of the loop, otherwise an SMEP fault could be reported as a false positive, when PDE.U=1 but PTE.U=0. Noted by Xiao Guangrong. The result is short, branch-free code. Reviewed-by: Xiao Guangrong <xiaoguangrong@linux.vnet.ibm.com> Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'arch/x86/kvm/mmu.c')
-rw-r--r--arch/x86/kvm/mmu.c38
1 files changed, 38 insertions, 0 deletions
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index f297a2ccf4f6..9c6188931f87 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -3516,6 +3516,38 @@ static void reset_rsvds_bits_mask(struct kvm_vcpu *vcpu,
3516 } 3516 }
3517} 3517}
3518 3518
3519static void update_permission_bitmask(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu)
3520{
3521 unsigned bit, byte, pfec;
3522 u8 map;
3523 bool fault, x, w, u, wf, uf, ff, smep;
3524
3525 smep = kvm_read_cr4_bits(vcpu, X86_CR4_SMEP);
3526 for (byte = 0; byte < ARRAY_SIZE(mmu->permissions); ++byte) {
3527 pfec = byte << 1;
3528 map = 0;
3529 wf = pfec & PFERR_WRITE_MASK;
3530 uf = pfec & PFERR_USER_MASK;
3531 ff = pfec & PFERR_FETCH_MASK;
3532 for (bit = 0; bit < 8; ++bit) {
3533 x = bit & ACC_EXEC_MASK;
3534 w = bit & ACC_WRITE_MASK;
3535 u = bit & ACC_USER_MASK;
3536
3537 /* Not really needed: !nx will cause pte.nx to fault */
3538 x |= !mmu->nx;
3539 /* Allow supervisor writes if !cr0.wp */
3540 w |= !is_write_protection(vcpu) && !uf;
3541 /* Disallow supervisor fetches of user code if cr4.smep */
3542 x &= !(smep && u && !uf);
3543
3544 fault = (ff && !x) || (uf && !u) || (wf && !w);
3545 map |= fault << bit;
3546 }
3547 mmu->permissions[byte] = map;
3548 }
3549}
3550
3519static int paging64_init_context_common(struct kvm_vcpu *vcpu, 3551static int paging64_init_context_common(struct kvm_vcpu *vcpu,
3520 struct kvm_mmu *context, 3552 struct kvm_mmu *context,
3521 int level) 3553 int level)
@@ -3524,6 +3556,7 @@ static int paging64_init_context_common(struct kvm_vcpu *vcpu,
3524 context->root_level = level; 3556 context->root_level = level;
3525 3557
3526 reset_rsvds_bits_mask(vcpu, context); 3558 reset_rsvds_bits_mask(vcpu, context);
3559 update_permission_bitmask(vcpu, context);
3527 3560
3528 ASSERT(is_pae(vcpu)); 3561 ASSERT(is_pae(vcpu));
3529 context->new_cr3 = paging_new_cr3; 3562 context->new_cr3 = paging_new_cr3;
@@ -3552,6 +3585,7 @@ static int paging32_init_context(struct kvm_vcpu *vcpu,
3552 context->root_level = PT32_ROOT_LEVEL; 3585 context->root_level = PT32_ROOT_LEVEL;
3553 3586
3554 reset_rsvds_bits_mask(vcpu, context); 3587 reset_rsvds_bits_mask(vcpu, context);
3588 update_permission_bitmask(vcpu, context);
3555 3589
3556 context->new_cr3 = paging_new_cr3; 3590 context->new_cr3 = paging_new_cr3;
3557 context->page_fault = paging32_page_fault; 3591 context->page_fault = paging32_page_fault;
@@ -3612,6 +3646,8 @@ static int init_kvm_tdp_mmu(struct kvm_vcpu *vcpu)
3612 context->gva_to_gpa = paging32_gva_to_gpa; 3646 context->gva_to_gpa = paging32_gva_to_gpa;
3613 } 3647 }
3614 3648
3649 update_permission_bitmask(vcpu, context);
3650
3615 return 0; 3651 return 0;
3616} 3652}
3617 3653
@@ -3687,6 +3723,8 @@ static int init_kvm_nested_mmu(struct kvm_vcpu *vcpu)
3687 g_context->gva_to_gpa = paging32_gva_to_gpa_nested; 3723 g_context->gva_to_gpa = paging32_gva_to_gpa_nested;
3688 } 3724 }
3689 3725
3726 update_permission_bitmask(vcpu, g_context);
3727
3690 return 0; 3728 return 0;
3691} 3729}
3692 3730