diff options
Diffstat (limited to 'accel/tcg/cputlb.c')
| -rw-r--r-- | accel/tcg/cputlb.c | 43 |
1 files changed, 30 insertions, 13 deletions
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 008ae7a66d..e984a98dc4 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -1250,7 +1250,6 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx, desc->fulltlb[index] = *full; desc->fulltlb[index].xlat_section = iotlb - vaddr_page; desc->fulltlb[index].phys_addr = paddr_page; - desc->fulltlb[index].prot = prot; /* Now calculate the new entry */ tn.addend = addend - vaddr_page; @@ -1768,6 +1767,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, CPUTLBEntry *tlbe; target_ulong tlb_addr; void *hostaddr; + CPUTLBEntryFull *full; tcg_debug_assert(mmu_idx < NB_MMU_MODES); @@ -1806,17 +1806,26 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, tlb_addr = tlb_addr_write(tlbe) & ~TLB_INVALID_MASK; } - /* Let the guest notice RMW on a write-only page. */ - if ((prot & PAGE_READ) && - unlikely(tlbe->addr_read != (tlb_addr & ~TLB_NOTDIRTY))) { - tlb_fill(env_cpu(env), addr, size, - MMU_DATA_LOAD, mmu_idx, retaddr); + if (prot & PAGE_READ) { /* - * Since we don't support reads and writes to different addresses, - * and we do have the proper page loaded for write, this shouldn't - * ever return. But just in case, handle via stop-the-world. + * Let the guest notice RMW on a write-only page. + * We have just verified that the page is writable. + * Subpage lookups may have left TLB_INVALID_MASK set, + * but addr_read will only be -1 if PAGE_READ was unset. */ - goto stop_the_world; + if (unlikely(tlbe->addr_read == -1)) { + tlb_fill(env_cpu(env), addr, size, + MMU_DATA_LOAD, mmu_idx, retaddr); + /* + * Since we don't support reads and writes to different + * addresses, and we do have the proper page loaded for + * write, this shouldn't ever return. But just in case, + * handle via stop-the-world. + */ + goto stop_the_world; + } + /* Collect TLB_WATCHPOINT for read. */ + tlb_addr |= tlbe->addr_read; } } else /* if (prot & PAGE_READ) */ { tlb_addr = tlbe->addr_read; @@ -1832,17 +1841,25 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, } /* Notice an IO access or a needs-MMU-lookup access */ - if (unlikely(tlb_addr & TLB_MMIO)) { + if (unlikely(tlb_addr & (TLB_MMIO | TLB_DISCARD_WRITE))) { /* There's really nothing that can be done to support this apart from stop-the-world. */ goto stop_the_world; } hostaddr = (void *)((uintptr_t)addr + tlbe->addend); + full = &env_tlb(env)->d[mmu_idx].fulltlb[index]; if (unlikely(tlb_addr & TLB_NOTDIRTY)) { - notdirty_write(env_cpu(env), addr, size, - &env_tlb(env)->d[mmu_idx].fulltlb[index], retaddr); + notdirty_write(env_cpu(env), addr, size, full, retaddr); + } + + if (unlikely(tlb_addr & TLB_WATCHPOINT)) { + QEMU_BUILD_BUG_ON(PAGE_READ != BP_MEM_READ); + QEMU_BUILD_BUG_ON(PAGE_WRITE != BP_MEM_WRITE); + /* therefore prot == watchpoint bits */ + cpu_check_watchpoint(env_cpu(env), addr, size, + full->attrs, prot, retaddr); } return hostaddr; |