target-i386: do not duplicate page protection checks

x86_cpu_handle_mmu_fault is currently checking twice for writability
and executability of pages; the first time to decide whether to
trigger a page fault, the second time to compute the "prot" argument
to tlb_set_page_with_attrs.

Reorganize code so that first "prot" is computed, then it is used
to check whether to raise a page fault, then finally PROT_WRITE is
removed if the D bit will have to be set.

Backports commit 76c64d33601a4948d6f72022992574a75b6fab97 from qemu
This commit is contained in:
Paolo Bonzini 2018-02-17 20:59:42 -05:00 committed by Lioncash
parent 1e3e75fa44
commit 3dab621825
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7

View file

@ -703,38 +703,28 @@ do_check_protect_pse36:
goto do_fault_rsvd; goto do_fault_rsvd;
} }
ptep ^= PG_NX_MASK; ptep ^= PG_NX_MASK;
if ((ptep & PG_NX_MASK) && is_write1 == 2) { /* can the page can be put in the TLB? prot will tell us */
if (is_user && !(ptep & PG_USER_MASK)) {
goto do_fault_protect; goto do_fault_protect;
} }
switch (mmu_idx) { prot = 0;
case MMU_USER_IDX: if (mmu_idx != MMU_KSMAP_IDX || !(ptep & PG_USER_MASK)) {
if (!(ptep & PG_USER_MASK)) { prot |= PAGE_READ;
goto do_fault_protect; if ((ptep & PG_RW_MASK) || (!is_user && !(env->cr[0] & CR0_WP_MASK))) {
} prot |= PAGE_WRITE;
if (is_write && !(ptep & PG_RW_MASK)) {
goto do_fault_protect;
}
break;
case MMU_KSMAP_IDX:
if (is_write1 != 2 && (ptep & PG_USER_MASK)) {
goto do_fault_protect;
} }
/* fall through */
case MMU_KNOSMAP_IDX:
if (is_write1 == 2 && (env->cr[4] & CR4_SMEP_MASK) &&
(ptep & PG_USER_MASK)) {
goto do_fault_protect;
}
if ((env->cr[0] & CR0_WP_MASK) &&
is_write && !(ptep & PG_RW_MASK)) {
goto do_fault_protect;
}
break;
default: /* cannot happen */
break;
} }
if (!(ptep & PG_NX_MASK) &&
(mmu_idx == MMU_USER_IDX ||
!((env->cr[4] & CR4_SMEP_MASK) && (ptep & PG_USER_MASK)))) {
prot |= PAGE_EXEC;
}
if ((prot & (1 << is_write1)) == 0) {
goto do_fault_protect;
}
/* yes, it can! */
is_dirty = is_write && !(pte & PG_DIRTY_MASK); is_dirty = is_write && !(pte & PG_DIRTY_MASK);
if (!(pte & PG_ACCESSED_MASK) || is_dirty) { if (!(pte & PG_ACCESSED_MASK) || is_dirty) {
pte |= PG_ACCESSED_MASK; pte |= PG_ACCESSED_MASK;
@ -744,27 +734,14 @@ do_check_protect_pse36:
x86_stl_phys_notdirty(cs, pte_addr, pte); x86_stl_phys_notdirty(cs, pte_addr, pte);
} }
/* the page can be put in the TLB */ if (!(pte & PG_DIRTY_MASK)) {
prot = PAGE_READ;
if (!(ptep & PG_NX_MASK) &&
(mmu_idx == MMU_USER_IDX ||
!((env->cr[4] & CR4_SMEP_MASK) && (ptep & PG_USER_MASK)))) {
prot |= PAGE_EXEC;
}
if (pte & PG_DIRTY_MASK) {
/* only set write access if already dirty... otherwise wait /* only set write access if already dirty... otherwise wait
for dirty access */ for dirty access */
if (is_user) { assert(!is_write);
if (ptep & PG_RW_MASK) prot &= ~PAGE_WRITE;
prot |= PAGE_WRITE;
} else {
if (!(env->cr[0] & CR0_WP_MASK) ||
(ptep & PG_RW_MASK))
prot |= PAGE_WRITE;
}
} }
do_mapping:
do_mapping:
#if 0 #if 0
pte = pte & env->a20_mask; pte = pte & env->a20_mask;
@ -783,6 +760,7 @@ do_check_protect_pse36:
paddr = vaddr; paddr = vaddr;
//printf(">>> map address %"PRIx64" to %"PRIx64"\n", vaddr, paddr); //printf(">>> map address %"PRIx64" to %"PRIx64"\n", vaddr, paddr);
assert(prot & (1 << is_write1));
tlb_set_page_with_attrs(cs, vaddr, paddr, cpu_get_mem_attrs(env), tlb_set_page_with_attrs(cs, vaddr, paddr, cpu_get_mem_attrs(env),
prot, mmu_idx, page_size); prot, mmu_idx, page_size);
return 0; return 0;