Skip to content

Commit

Permalink
target/arm: Handle watchpoints in sve_ld1_r
Browse files Browse the repository at this point in the history
Handle all of the watchpoints for active elements all at once,
before we've modified the vector register.  This removes the
TLB_WATCHPOINT bit from page[].flags, which means that we can
use the normal fast path via RAM.

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20200508154359.7494-13-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
  • Loading branch information
rth7680 authored and pm215 committed May 11, 2020
1 parent b854fd0 commit 4bcc3f0
Showing 1 changed file with 71 additions and 1 deletion.
72 changes: 71 additions & 1 deletion target/arm/sve_helper.c
Expand Up @@ -4371,6 +4371,70 @@ static bool sve_cont_ldst_pages(SVEContLdSt *info, SVEContFault fault,
return have_work;
}

static void sve_cont_ldst_watchpoints(SVEContLdSt *info, CPUARMState *env,
uint64_t *vg, target_ulong addr,
int esize, int msize, int wp_access,
uintptr_t retaddr)
{
#ifndef CONFIG_USER_ONLY
intptr_t mem_off, reg_off, reg_last;
int flags0 = info->page[0].flags;
int flags1 = info->page[1].flags;

if (likely(!((flags0 | flags1) & TLB_WATCHPOINT))) {
return;
}

/* Indicate that watchpoints are handled. */
info->page[0].flags = flags0 & ~TLB_WATCHPOINT;
info->page[1].flags = flags1 & ~TLB_WATCHPOINT;

if (flags0 & TLB_WATCHPOINT) {
mem_off = info->mem_off_first[0];
reg_off = info->reg_off_first[0];
reg_last = info->reg_off_last[0];

while (reg_off <= reg_last) {
uint64_t pg = vg[reg_off >> 6];
do {
if ((pg >> (reg_off & 63)) & 1) {
cpu_check_watchpoint(env_cpu(env), addr + mem_off,
msize, info->page[0].attrs,
wp_access, retaddr);
}
reg_off += esize;
mem_off += msize;
} while (reg_off <= reg_last && (reg_off & 63));
}
}

mem_off = info->mem_off_split;
if (mem_off >= 0) {
cpu_check_watchpoint(env_cpu(env), addr + mem_off, msize,
info->page[0].attrs, wp_access, retaddr);
}

mem_off = info->mem_off_first[1];
if ((flags1 & TLB_WATCHPOINT) && mem_off >= 0) {
reg_off = info->reg_off_first[1];
reg_last = info->reg_off_last[1];

do {
uint64_t pg = vg[reg_off >> 6];
do {
if ((pg >> (reg_off & 63)) & 1) {
cpu_check_watchpoint(env_cpu(env), addr + mem_off,
msize, info->page[1].attrs,
wp_access, retaddr);
}
reg_off += esize;
mem_off += msize;
} while (reg_off & 63);
} while (reg_off <= reg_last);
}
#endif
}

/*
* The result of tlb_vaddr_to_host for user-only is just g2h(x),
* which is always non-null. Elide the useless test.
Expand Down Expand Up @@ -4412,13 +4476,19 @@ void sve_ld1_r(CPUARMState *env, uint64_t *vg, const target_ulong addr,
/* Probe the page(s). Exit with exception for any invalid page. */
sve_cont_ldst_pages(&info, FAULT_ALL, env, addr, MMU_DATA_LOAD, retaddr);

/* Handle watchpoints for all active elements. */
sve_cont_ldst_watchpoints(&info, env, vg, addr, 1 << esz, 1 << msz,
BP_MEM_READ, retaddr);

/* TODO: MTE check. */

flags = info.page[0].flags | info.page[1].flags;
if (unlikely(flags != 0)) {
#ifdef CONFIG_USER_ONLY
g_assert_not_reached();
#else
/*
* At least one page includes MMIO (or watchpoints).
* At least one page includes MMIO.
* Any bus operation can fail with cpu_transaction_failed,
* which for ARM will raise SyncExternal. Perform the load
* into scratch memory to preserve register state until the end.
Expand Down

0 comments on commit 4bcc3f0

Please sign in to comment.