]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - arch/x86/kvm/mmu.c
KVM: MMU: speedup update_permission_bitmask
[mirror_ubuntu-bionic-kernel.git] / arch / x86 / kvm / mmu.c
index f47cccace1a1cf4f1fd23a03d360b277a8e83888..2a8a6e3e2a312441761dcbd15d2af8d6a5235fcd 100644 (file)
@@ -4204,66 +4204,85 @@ reset_ept_shadow_zero_bits_mask(struct kvm_vcpu *vcpu,
                                    boot_cpu_data.x86_phys_bits, execonly);
 }
 
+#define BYTE_MASK(access) \
+       ((1 & (access) ? 2 : 0) | \
+        (2 & (access) ? 4 : 0) | \
+        (3 & (access) ? 8 : 0) | \
+        (4 & (access) ? 16 : 0) | \
+        (5 & (access) ? 32 : 0) | \
+        (6 & (access) ? 64 : 0) | \
+        (7 & (access) ? 128 : 0))
+
+
 static void update_permission_bitmask(struct kvm_vcpu *vcpu,
                                      struct kvm_mmu *mmu, bool ept)
 {
-       unsigned bit, byte, pfec;
-       u8 map;
-       bool fault, x, w, u, wf, uf, ff, smapf, cr4_smap, cr4_smep, smap = 0;
+       unsigned byte;
+
+       const u8 x = BYTE_MASK(ACC_EXEC_MASK);
+       const u8 w = BYTE_MASK(ACC_WRITE_MASK);
+       const u8 u = BYTE_MASK(ACC_USER_MASK);
+
+       bool cr4_smep = kvm_read_cr4_bits(vcpu, X86_CR4_SMEP) != 0;
+       bool cr4_smap = kvm_read_cr4_bits(vcpu, X86_CR4_SMAP) != 0;
+       bool cr0_wp = is_write_protection(vcpu);
 
-       cr4_smep = kvm_read_cr4_bits(vcpu, X86_CR4_SMEP);
-       cr4_smap = kvm_read_cr4_bits(vcpu, X86_CR4_SMAP);
        for (byte = 0; byte < ARRAY_SIZE(mmu->permissions); ++byte) {
-               pfec = byte << 1;
-               map = 0;
-               wf = pfec & PFERR_WRITE_MASK;
-               uf = pfec & PFERR_USER_MASK;
-               ff = pfec & PFERR_FETCH_MASK;
+               unsigned pfec = byte << 1;
+
                /*
-                * PFERR_RSVD_MASK bit is set in PFEC if the access is not
-                * subject to SMAP restrictions, and cleared otherwise. The
-                * bit is only meaningful if the SMAP bit is set in CR4.
+                * Each "*f" variable has a 1 bit for each UWX value
+                * that causes a fault with the given PFEC.
                 */
-               smapf = !(pfec & PFERR_RSVD_MASK);
-               for (bit = 0; bit < 8; ++bit) {
-                       x = bit & ACC_EXEC_MASK;
-                       w = bit & ACC_WRITE_MASK;
-                       u = bit & ACC_USER_MASK;
-
-                       if (!ept) {
-                               /* Not really needed: !nx will cause pte.nx to fault */
-                               x |= !mmu->nx;
-                               /* Allow supervisor writes if !cr0.wp */
-                               w |= !is_write_protection(vcpu) && !uf;
-                               /* Disallow supervisor fetches of user code if cr4.smep */
-                               x &= !(cr4_smep && u && !uf);
-
-                               /*
-                                * SMAP:kernel-mode data accesses from user-mode
-                                * mappings should fault. A fault is considered
-                                * as a SMAP violation if all of the following
-                                * conditions are ture:
-                                *   - X86_CR4_SMAP is set in CR4
-                                *   - A user page is accessed
-                                *   - Page fault in kernel mode
-                                *   - if CPL = 3 or X86_EFLAGS_AC is clear
-                                *
-                                *   Here, we cover the first three conditions.
-                                *   The fourth is computed dynamically in
-                                *   permission_fault() and is in smapf.
-                                *
-                                *   Also, SMAP does not affect instruction
-                                *   fetches, add the !ff check here to make it
-                                *   clearer.
-                                */
-                               smap = cr4_smap && u && !uf && !ff;
-                       }
 
-                       fault = (ff && !x) || (uf && !u) || (wf && !w) ||
-                               (smapf && smap);
-                       map |= fault << bit;
+               /* Faults from writes to non-writable pages */
+               u8 wf = (pfec & PFERR_WRITE_MASK) ? ~w : 0;
+               /* Faults from user mode accesses to supervisor pages */
+               u8 uf = (pfec & PFERR_USER_MASK) ? ~u : 0;
+               /* Faults from fetches of non-executable pages*/
+               u8 ff = (pfec & PFERR_FETCH_MASK) ? ~x : 0;
+               /* Faults from kernel mode fetches of user pages */
+               u8 smepf = 0;
+               /* Faults from kernel mode accesses of user pages */
+               u8 smapf = 0;
+
+               if (!ept) {
+                       /* Faults from kernel mode accesses to user pages */
+                       u8 kf = (pfec & PFERR_USER_MASK) ? 0 : u;
+
+                       /* Not really needed: !nx will cause pte.nx to fault */
+                       if (!mmu->nx)
+                               ff = 0;
+
+                       /* Allow supervisor writes if !cr0.wp */
+                       if (!cr0_wp)
+                               wf = (pfec & PFERR_USER_MASK) ? wf : 0;
+
+                       /* Disallow supervisor fetches of user code if cr4.smep */
+                       if (cr4_smep)
+                               smepf = (pfec & PFERR_FETCH_MASK) ? kf : 0;
+
+                       /*
+                        * SMAP:kernel-mode data accesses from user-mode
+                        * mappings should fault. A fault is considered
+                        * as a SMAP violation if all of the following
+                        * conditions are ture:
+                        *   - X86_CR4_SMAP is set in CR4
+                        *   - A user page is accessed
+                        *   - The access is not a fetch
+                        *   - Page fault in kernel mode
+                        *   - if CPL = 3 or X86_EFLAGS_AC is clear
+                        *
+                        * Here, we cover the first three conditions.
+                        * The fourth is computed dynamically in permission_fault();
+                        * PFERR_RSVD_MASK bit will be set in PFEC if the access is
+                        * *not* subject to SMAP restrictions.
+                        */
+                       if (cr4_smap)
+                               smapf = (pfec & (PFERR_RSVD_MASK|PFERR_FETCH_MASK)) ? 0 : kf;
                }
-               mmu->permissions[byte] = map;
+
+               mmu->permissions[byte] = ff | uf | wf | smepf | smapf;
        }
 }