diff options
Diffstat (limited to 'accel')
-rw-r--r-- | accel/tcg/cputlb.c | 40 |
1 files changed, 29 insertions, 11 deletions
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index b49fb0d..e984a98 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -1767,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); @@ -1805,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; @@ -1838,10 +1848,18 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, } 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; |