diff options
| author | Feng Wu <feng.wu@intel.com> | 2014-04-01 05:46:34 -0400 |
|---|---|---|
| committer | Marcelo Tosatti <mtosatti@redhat.com> | 2014-04-14 16:50:34 -0400 |
| commit | 97ec8c067d322d32effdc1701760d3babbc5595f (patch) | |
| tree | 16892fd16ce9e3d60c68b01f7bf11a8821eb3295 | |
| parent | 56d6efc2de5fcf76d3c7b33a7671bc04c53cb0e5 (diff) | |
KVM: Add SMAP support when setting CR4
This patch adds SMAP handling logic when setting CR4 for guests
Thanks a lot to Paolo Bonzini for his suggestion to use the branchless
way to detect SMAP violation.
Signed-off-by: Feng Wu <feng.wu@intel.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
| -rw-r--r-- | arch/x86/kvm/cpuid.h | 8 | ||||
| -rw-r--r-- | arch/x86/kvm/mmu.c | 34 | ||||
| -rw-r--r-- | arch/x86/kvm/mmu.h | 44 | ||||
| -rw-r--r-- | arch/x86/kvm/paging_tmpl.h | 2 | ||||
| -rw-r--r-- | arch/x86/kvm/x86.c | 9 |
5 files changed, 84 insertions, 13 deletions
diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h index a2a1bb7ed8c1..eeecbed26ac7 100644 --- a/arch/x86/kvm/cpuid.h +++ b/arch/x86/kvm/cpuid.h | |||
| @@ -48,6 +48,14 @@ static inline bool guest_cpuid_has_smep(struct kvm_vcpu *vcpu) | |||
| 48 | return best && (best->ebx & bit(X86_FEATURE_SMEP)); | 48 | return best && (best->ebx & bit(X86_FEATURE_SMEP)); |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | static inline bool guest_cpuid_has_smap(struct kvm_vcpu *vcpu) | ||
| 52 | { | ||
| 53 | struct kvm_cpuid_entry2 *best; | ||
| 54 | |||
| 55 | best = kvm_find_cpuid_entry(vcpu, 7, 0); | ||
| 56 | return best && (best->ebx & bit(X86_FEATURE_SMAP)); | ||
| 57 | } | ||
| 58 | |||
| 51 | static inline bool guest_cpuid_has_fsgsbase(struct kvm_vcpu *vcpu) | 59 | static inline bool guest_cpuid_has_fsgsbase(struct kvm_vcpu *vcpu) |
| 52 | { | 60 | { |
| 53 | struct kvm_cpuid_entry2 *best; | 61 | struct kvm_cpuid_entry2 *best; |
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index f5704d9e5ddc..084caf3efaf5 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c | |||
| @@ -3601,20 +3601,27 @@ static void reset_rsvds_bits_mask_ept(struct kvm_vcpu *vcpu, | |||
| 3601 | } | 3601 | } |
| 3602 | } | 3602 | } |
| 3603 | 3603 | ||
| 3604 | static void update_permission_bitmask(struct kvm_vcpu *vcpu, | 3604 | void update_permission_bitmask(struct kvm_vcpu *vcpu, |
| 3605 | struct kvm_mmu *mmu, bool ept) | 3605 | struct kvm_mmu *mmu, bool ept) |
| 3606 | { | 3606 | { |
| 3607 | unsigned bit, byte, pfec; | 3607 | unsigned bit, byte, pfec; |
| 3608 | u8 map; | 3608 | u8 map; |
| 3609 | bool fault, x, w, u, wf, uf, ff, smep; | 3609 | bool fault, x, w, u, wf, uf, ff, smapf, cr4_smap, smep, smap = 0; |
| 3610 | 3610 | ||
| 3611 | smep = kvm_read_cr4_bits(vcpu, X86_CR4_SMEP); | 3611 | smep = kvm_read_cr4_bits(vcpu, X86_CR4_SMEP); |
| 3612 | cr4_smap = kvm_read_cr4_bits(vcpu, X86_CR4_SMAP); | ||
| 3612 | for (byte = 0; byte < ARRAY_SIZE(mmu->permissions); ++byte) { | 3613 | for (byte = 0; byte < ARRAY_SIZE(mmu->permissions); ++byte) { |
| 3613 | pfec = byte << 1; | 3614 | pfec = byte << 1; |
| 3614 | map = 0; | 3615 | map = 0; |
| 3615 | wf = pfec & PFERR_WRITE_MASK; | 3616 | wf = pfec & PFERR_WRITE_MASK; |
| 3616 | uf = pfec & PFERR_USER_MASK; | 3617 | uf = pfec & PFERR_USER_MASK; |
| 3617 | ff = pfec & PFERR_FETCH_MASK; | 3618 | ff = pfec & PFERR_FETCH_MASK; |
| 3619 | /* | ||
| 3620 | * PFERR_RSVD_MASK bit is set in PFEC if the access is not | ||
| 3621 | * subject to SMAP restrictions, and cleared otherwise. The | ||
| 3622 | * bit is only meaningful if the SMAP bit is set in CR4. | ||
| 3623 | */ | ||
| 3624 | smapf = !(pfec & PFERR_RSVD_MASK); | ||
| 3618 | for (bit = 0; bit < 8; ++bit) { | 3625 | for (bit = 0; bit < 8; ++bit) { |
| 3619 | x = bit & ACC_EXEC_MASK; | 3626 | x = bit & ACC_EXEC_MASK; |
| 3620 | w = bit & ACC_WRITE_MASK; | 3627 | w = bit & ACC_WRITE_MASK; |
| @@ -3627,11 +3634,32 @@ static void update_permission_bitmask(struct kvm_vcpu *vcpu, | |||
| 3627 | w |= !is_write_protection(vcpu) && !uf; | 3634 | w |= !is_write_protection(vcpu) && !uf; |
| 3628 | /* Disallow supervisor fetches of user code if cr4.smep */ | 3635 | /* Disallow supervisor fetches of user code if cr4.smep */ |
| 3629 | x &= !(smep && u && !uf); | 3636 | x &= !(smep && u && !uf); |
| 3637 | |||
| 3638 | /* | ||
| 3639 | * SMAP:kernel-mode data accesses from user-mode | ||
| 3640 | * mappings should fault. A fault is considered | ||
| 3641 | * as a SMAP violation if all of the following | ||
| 3642 | * conditions are ture: | ||
| 3643 | * - X86_CR4_SMAP is set in CR4 | ||
| 3644 | * - An user page is accessed | ||
| 3645 | * - Page fault in kernel mode | ||
| 3646 | * - if CPL = 3 or X86_EFLAGS_AC is clear | ||
| 3647 | * | ||
| 3648 | * Here, we cover the first three conditions. | ||
| 3649 | * The fourth is computed dynamically in | ||
| 3650 | * permission_fault() and is in smapf. | ||
| 3651 | * | ||
| 3652 | * Also, SMAP does not affect instruction | ||
| 3653 | * fetches, add the !ff check here to make it | ||
| 3654 | * clearer. | ||
| 3655 | */ | ||
| 3656 | smap = cr4_smap && u && !uf && !ff; | ||
| 3630 | } else | 3657 | } else |
| 3631 | /* Not really needed: no U/S accesses on ept */ | 3658 | /* Not really needed: no U/S accesses on ept */ |
| 3632 | u = 1; | 3659 | u = 1; |
| 3633 | 3660 | ||
| 3634 | fault = (ff && !x) || (uf && !u) || (wf && !w); | 3661 | fault = (ff && !x) || (uf && !u) || (wf && !w) || |
| 3662 | (smapf && smap); | ||
| 3635 | map |= fault << bit; | 3663 | map |= fault << bit; |
| 3636 | } | 3664 | } |
| 3637 | mmu->permissions[byte] = map; | 3665 | mmu->permissions[byte] = map; |
diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index 292615274358..3842e70bdb7c 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h | |||
| @@ -44,11 +44,17 @@ | |||
| 44 | #define PT_DIRECTORY_LEVEL 2 | 44 | #define PT_DIRECTORY_LEVEL 2 |
| 45 | #define PT_PAGE_TABLE_LEVEL 1 | 45 | #define PT_PAGE_TABLE_LEVEL 1 |
| 46 | 46 | ||
| 47 | #define PFERR_PRESENT_MASK (1U << 0) | 47 | #define PFERR_PRESENT_BIT 0 |
| 48 | #define PFERR_WRITE_MASK (1U << 1) | 48 | #define PFERR_WRITE_BIT 1 |
| 49 | #define PFERR_USER_MASK (1U << 2) | 49 | #define PFERR_USER_BIT 2 |
| 50 | #define PFERR_RSVD_MASK (1U << 3) | 50 | #define PFERR_RSVD_BIT 3 |
| 51 | #define PFERR_FETCH_MASK (1U << 4) | 51 | #define PFERR_FETCH_BIT 4 |
| 52 | |||
| 53 | #define PFERR_PRESENT_MASK (1U << PFERR_PRESENT_BIT) | ||
| 54 | #define PFERR_WRITE_MASK (1U << PFERR_WRITE_BIT) | ||
| 55 | #define PFERR_USER_MASK (1U << PFERR_USER_BIT) | ||
| 56 | #define PFERR_RSVD_MASK (1U << PFERR_RSVD_BIT) | ||
| 57 | #define PFERR_FETCH_MASK (1U << PFERR_FETCH_BIT) | ||
| 52 | 58 | ||
| 53 | int kvm_mmu_get_spte_hierarchy(struct kvm_vcpu *vcpu, u64 addr, u64 sptes[4]); | 59 | int kvm_mmu_get_spte_hierarchy(struct kvm_vcpu *vcpu, u64 addr, u64 sptes[4]); |
| 54 | void kvm_mmu_set_mmio_spte_mask(u64 mmio_mask); | 60 | void kvm_mmu_set_mmio_spte_mask(u64 mmio_mask); |
| @@ -73,6 +79,8 @@ int handle_mmio_page_fault_common(struct kvm_vcpu *vcpu, u64 addr, bool direct); | |||
| 73 | void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *context); | 79 | void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *context); |
| 74 | void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *context, | 80 | void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *context, |
| 75 | bool execonly); | 81 | bool execonly); |
| 82 | void update_permission_bitmask(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, | ||
| 83 | bool ept); | ||
| 76 | 84 | ||
| 77 | static inline unsigned int kvm_mmu_available_pages(struct kvm *kvm) | 85 | static inline unsigned int kvm_mmu_available_pages(struct kvm *kvm) |
| 78 | { | 86 | { |
| @@ -110,10 +118,30 @@ static inline bool is_write_protection(struct kvm_vcpu *vcpu) | |||
| 110 | * Will a fault with a given page-fault error code (pfec) cause a permission | 118 | * Will a fault with a given page-fault error code (pfec) cause a permission |
| 111 | * fault with the given access (in ACC_* format)? | 119 | * fault with the given access (in ACC_* format)? |
| 112 | */ | 120 | */ |
| 113 | static inline bool permission_fault(struct kvm_mmu *mmu, unsigned pte_access, | 121 | static inline bool permission_fault(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, |
| 114 | unsigned pfec) | 122 | unsigned pte_access, unsigned pfec) |
| 115 | { | 123 | { |
| 116 | return (mmu->permissions[pfec >> 1] >> pte_access) & 1; | 124 | int cpl = kvm_x86_ops->get_cpl(vcpu); |
| 125 | unsigned long rflags = kvm_x86_ops->get_rflags(vcpu); | ||
| 126 | |||
| 127 | /* | ||
| 128 | * If CPL < 3, SMAP prevention are disabled if EFLAGS.AC = 1. | ||
| 129 | * | ||
| 130 | * If CPL = 3, SMAP applies to all supervisor-mode data accesses | ||
| 131 | * (these are implicit supervisor accesses) regardless of the value | ||
| 132 | * of EFLAGS.AC. | ||
| 133 | * | ||
| 134 | * This computes (cpl < 3) && (rflags & X86_EFLAGS_AC), leaving | ||
| 135 | * the result in X86_EFLAGS_AC. We then insert it in place of | ||
| 136 | * the PFERR_RSVD_MASK bit; this bit will always be zero in pfec, | ||
| 137 | * but it will be one in index if SMAP checks are being overridden. | ||
| 138 | * It is important to keep this branchless. | ||
| 139 | */ | ||
| 140 | unsigned long smap = (cpl - 3) & (rflags & X86_EFLAGS_AC); | ||
| 141 | int index = (pfec >> 1) + | ||
| 142 | (smap >> (X86_EFLAGS_AC_BIT - PFERR_RSVD_BIT + 1)); | ||
| 143 | |||
| 144 | return (mmu->permissions[index] >> pte_access) & 1; | ||
| 117 | } | 145 | } |
| 118 | 146 | ||
| 119 | void kvm_mmu_invalidate_zap_all_pages(struct kvm *kvm); | 147 | void kvm_mmu_invalidate_zap_all_pages(struct kvm *kvm); |
diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h index b1e6c1bf68d3..123efd3ec29f 100644 --- a/arch/x86/kvm/paging_tmpl.h +++ b/arch/x86/kvm/paging_tmpl.h | |||
| @@ -353,7 +353,7 @@ retry_walk: | |||
| 353 | walker->ptes[walker->level - 1] = pte; | 353 | walker->ptes[walker->level - 1] = pte; |
| 354 | } while (!is_last_gpte(mmu, walker->level, pte)); | 354 | } while (!is_last_gpte(mmu, walker->level, pte)); |
| 355 | 355 | ||
| 356 | if (unlikely(permission_fault(mmu, pte_access, access))) { | 356 | if (unlikely(permission_fault(vcpu, mmu, pte_access, access))) { |
| 357 | errcode |= PFERR_PRESENT_MASK; | 357 | errcode |= PFERR_PRESENT_MASK; |
| 358 | goto error; | 358 | goto error; |
| 359 | } | 359 | } |
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index d1c55f8722c6..41693787cd66 100644 --- a/arch/x86/kvm/x86.c +++ b/ | |||
