Signed-off-by: Richard Henderson <[email protected]>
---
include/accel/tcg/probe.h | 24 +++++++++++++++++-------
accel/tcg/cputlb.c | 15 ++++++++-------
accel/tcg/user-exec.c | 7 +++++--
semihosting/uaccess.c | 10 ++++------
target/arm/tcg/sve_helper.c | 5 ++++-
target/riscv/tcg/op_helper.c | 4 ++--
target/riscv/tcg/vector_helper.c | 21 ++++++++++++---------
target/s390x/tcg/mem_helper.c | 4 ++--
8 files changed, 54 insertions(+), 36 deletions(-)
diff --git a/include/accel/tcg/probe.h b/include/accel/tcg/probe.h
index e3068a79de..06faf1269d 100644
--- a/include/accel/tcg/probe.h
+++ b/include/accel/tcg/probe.h
@@ -49,23 +49,33 @@ static inline void *probe_read(CPUArchState *env, vaddr
addr, int size,
/**
* probe_access_flags:
* @env: CPUArchState
- * @addr: guest virtual address to look up
- * @size: size of the access
+ * @addr: virtual address
+ * @first: beginning of virtual address range
+ * @last: end of virtual address range
* @access_type: read, write or execute permission
* @mmu_idx: MMU index to use for lookup
* @nonfault: suppress the fault
* @phost: return value for host address
* @retaddr: return address for unwinding
*
- * Similar to probe_access, loosely returning the TLB_FLAGS_MASK for
- * the page, and storing the host address for RAM in @phost.
+ * Probe an access for [@first, @last], where @addr is somewhere
+ * in that range. Normally @addr == @first, but some targets have
+ * accesses which are forcibly aligned after an initial fault for an
+ * inaccessible page (e.g. Arm DC_ZVA, where [@first, @last] will be
+ * the bounds of the cacheline containing @addr).
+ *
+ * If the access does not satisfy @access_type:
+ * - if @nonfault is false, raise an exception at @addr
+ * - otherwise return TLB_INVALID_MASK.
+ *
+ * Otherwise, return the TLB_FLAGS_MASK for the page, and set @phost:
+ * - host address for @addr, if direct host accesses are allowed,
+ * - otherwise NULL.
*
- * If @nonfault is set, do not raise an exception but return TLB_INVALID_MASK.
* Do not handle watchpoints, but include TLB_WATCHPOINT in the returned flags.
* Do handle clean pages, so exclude TLB_NOTDIRY from the returned flags.
- * For simplicity, all "mmio-like" flags are folded to TLB_MMIO.
*/
-int probe_access_flags(CPUArchState *env, vaddr addr, int size,
+int probe_access_flags(CPUArchState *env, vaddr addr, vaddr first, vaddr last,
MMUAccessType access_type, int mmu_idx,
bool nonfault, void **phost, uintptr_t retaddr);
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
index 6299cc73a4..a192c50e90 100644
--- a/accel/tcg/cputlb.c
+++ b/accel/tcg/cputlb.c
@@ -1460,23 +1460,24 @@ int probe_access_full_mmu(CPUArchState *env, vaddr
addr, int size,
return flags;
}
-int probe_access_flags(CPUArchState *env, vaddr addr, int size,
+int probe_access_flags(CPUArchState *env, vaddr addr, vaddr first, vaddr last,
MMUAccessType access_type, int mmu_idx,
bool nonfault, void **phost, uintptr_t retaddr)
{
CPUTLBEntryFull *full;
int flags;
- g_assert(-(addr | TARGET_PAGE_MASK) >= size);
+ assert(first <= addr);
+ assert(addr <= last);
+ assert(((first ^ last) & TARGET_PAGE_MASK) == 0);
- flags = probe_access_internal(env_cpu(env), addr, size, access_type,
- mmu_idx, nonfault, phost, &full, retaddr,
- true);
+ flags = probe_access_internal(env_cpu(env), addr, last - addr + 1,
+ access_type, mmu_idx, nonfault,
+ phost, &full, retaddr, true);
/* Handle clean RAM pages. */
if (unlikely(flags & TLB_NOTDIRTY)) {
- int dirtysize = size == 0 ? 1 : size;
- notdirty_write(env_cpu(env), addr, dirtysize, full, retaddr);
+ notdirty_write(env_cpu(env), first, last - first + 1, full, retaddr);
flags &= ~TLB_NOTDIRTY;
}
diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c
index 3724e5d98e..44e9f7fa1a 100644
--- a/accel/tcg/user-exec.c
+++ b/accel/tcg/user-exec.c
@@ -790,13 +790,16 @@ static int probe_access_internal(CPUArchState *env, vaddr
addr,
cpu_loop_exit_sigsegv(env_cpu(env), addr, access_type, maperr, ra);
}
-int probe_access_flags(CPUArchState *env, vaddr addr, int size,
+int probe_access_flags(CPUArchState *env, vaddr addr, vaddr first, vaddr last,
MMUAccessType access_type, int mmu_idx,
bool nonfault, void **phost, uintptr_t ra)
{
int flags;
- g_assert(-(addr | TARGET_PAGE_MASK) >= size);
+ assert(first <= addr);
+ assert(addr <= first);
+ assert(((first ^ last) & TARGET_PAGE_MASK) == 0);
+
flags = probe_access_internal(env, addr, access_type, nonfault, ra);
*phost = (flags & TLB_INVALID_MASK) ? NULL : g2h_vaddr(env_cpu(env), addr);
return flags;
diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c
index ff944d8c2f..1efe8464d7 100644
--- a/semihosting/uaccess.c
+++ b/semihosting/uaccess.c
@@ -33,15 +33,13 @@ ssize_t uaccess_strlen_user(CPUArchState *env, vaddr addr)
size_t len = 0;
while (1) {
- size_t left_in_page;
+ vaddr last_in_page = addr | ~TARGET_PAGE_MASK;
+ size_t left_in_page = last_in_page - addr + 1;
int flags;
void *h;
- /* Find the number of bytes remaining in the page. */
- left_in_page = TARGET_PAGE_SIZE - (addr & ~TARGET_PAGE_MASK);
-
- flags = probe_access_flags(env, addr, 0, MMU_DATA_LOAD,
- mmu_idx, true, &h, 0);
+ flags = probe_access_flags(env, addr, addr, last_in_page,
+ MMU_DATA_LOAD, mmu_idx, true, &h, 0);
if (flags & TLB_INVALID_MASK) {
return -1;
}
diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c
index 4e77a824dd..b824933694 100644
--- a/target/arm/tcg/sve_helper.c
+++ b/target/arm/tcg/sve_helper.c
@@ -6036,7 +6036,10 @@ bool sve_probe_page(SVEHostPage *info, bool nofault,
CPUARMState *env,
addr = useronly_clean_ptr(addr);
#ifdef CONFIG_USER_ONLY
- flags = probe_access_flags(env, addr, 0, access_type, mmu_idx, nofault,
+ flags = probe_access_flags(env, addr,
+ addr & TARGET_PAGE_MASK,
+ addr | ~TARGET_PAGE_MASK,
+ access_type, mmu_idx, nofault,
&info->host, retaddr);
#else
CPUTLBEntryFull *full;
diff --git a/target/riscv/tcg/op_helper.c b/target/riscv/tcg/op_helper.c
index a7bd42d2c6..b18bf683f4 100644
--- a/target/riscv/tcg/op_helper.c
+++ b/target/riscv/tcg/op_helper.c
@@ -250,8 +250,8 @@ static void check_zicbom_access(CPURISCVState *env,
* addresses, whether a cache-block management instruction is
* permitted to access the cache block is UNSPECIFIED."
*/
- ret = probe_access_flags(env, address, cbomlen, MMU_DATA_LOAD,
- mmu_idx, true, &phost, ra);
+ ret = probe_access_flags(env, address, address, address + cbomlen - 1,
+ MMU_DATA_LOAD, mmu_idx, true, &phost, ra);
if (ret != TLB_INVALID_MASK) {
/* Success: readable */
return;
diff --git a/target/riscv/tcg/vector_helper.c b/target/riscv/tcg/vector_helper.c
index b7b3805a6d..87f2196bc7 100644
--- a/target/riscv/tcg/vector_helper.c
+++ b/target/riscv/tcg/vector_helper.c
@@ -704,7 +704,7 @@ vext_ldff(void *vd, target_ulong base, CPURISCVState *env,
uint32_t esz = 1 << log2_esz;
uint32_t msize = nf * esz;
uint32_t vma = vext_vma(desc);
- target_ulong addr, last, last_in_page, page_split, elems;
+ target_ulong addr, last, last_in_page, page_split, elems, adj, adj_last;
MemOpIdx oi = vext_oi(desc, log2_esz);
int mmu_index = get_mmuidx(oi);
bool first_active;
@@ -759,10 +759,10 @@ vext_ldff(void *vd, target_ulong base, CPURISCVState *env,
* Test whether the first page is accessible.
* If the first element is active, it must succeed.
*/
- flags = probe_access_flags(env, adjust_addr(env, addr),
- MIN(last, last_in_page) - addr + 1,
- MMU_DATA_LOAD, mmu_index, !first_active,
- &host, ra);
+ adj = adjust_addr(env, addr);
+ adj_last = MIN(last, last_in_page) - (addr - adj);
+ flags = probe_access_flags(env, adj, adj, adj_last, MMU_DATA_LOAD,
+ mmu_index, !first_active, &host, ra);
/* Get number of complete elements in the first page. */
elems = MIN(page_split / msize, vl - i);
@@ -853,8 +853,10 @@ vext_ldff(void *vd, target_ulong base, CPURISCVState *env,
* We have not yet advanced addr to the next page.
*/
target_ulong next_page = addr + page_split;
- flags |= probe_access_flags(env, adjust_addr(env, next_page),
- last - next_page + 1, MMU_DATA_LOAD,
+
+ adj = adjust_addr(env, next_page);
+ adj_last = last - (next_page - adj);
+ flags |= probe_access_flags(env, adj, adj, adj_last, MMU_DATA_LOAD,
mmu_index, true, &host, ra);
/* Stop if invalid (unmapped) or mmio (transaction may fail). */
@@ -873,8 +875,9 @@ vext_ldff(void *vd, target_ulong base, CPURISCVState *env,
host += addr - next_page;
}
} else {
- flags = probe_access_flags(env, adjust_addr(env, addr),
- last - addr + 1, MMU_DATA_LOAD,
+ adj = adjust_addr(env, addr);
+ adj_last = last - (addr - adj);
+ flags = probe_access_flags(env, adj, adj, adj_last, MMU_DATA_LOAD,
mmu_index, true, &host, ra);
/* Stop if invalid (unmapped) or mmio (transaction may fail). */
diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c
index 414e6838c8..d490434806 100644
--- a/target/s390x/tcg/mem_helper.c
+++ b/target/s390x/tcg/mem_helper.c
@@ -155,8 +155,8 @@ static inline int s390_probe_access(CPUArchState *env,
vaddr addr,
int mmu_idx, bool nonfault,
void **phost, uintptr_t ra)
{
- int flags = probe_access_flags(env, addr, size, access_type, mmu_idx,
- nonfault, phost, ra);
+ int flags = probe_access_flags(env, addr, addr, addr + size - 1,
+ access_type, mmu_idx, nonfault, phost, ra);
if (unlikely(flags & TLB_INVALID_MASK)) {
#ifdef CONFIG_USER_ONLY
--
2.43.0