mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-23 19:49:43 +00:00
accel/tcg: Add probe_access_flags
This new interface will allow targets to probe for a page and then handle watchpoints themselves. This will be most useful for vector predicated memory operations, where one page lookup can be used for many operations, and one test can avoid many watchpoint checks. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200508154359.7494-6-richard.henderson@linaro.org Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
7a1bfee682
commit
069cfe77d6
@ -1231,131 +1231,134 @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static int probe_access_internal(CPUArchState *env, target_ulong addr,
|
||||||
* Probe for whether the specified guest access is permitted. If it is not
|
int fault_size, MMUAccessType access_type,
|
||||||
* permitted then an exception will be taken in the same way as if this
|
int mmu_idx, bool nonfault,
|
||||||
* were a real access (and we will not return).
|
void **phost, uintptr_t retaddr)
|
||||||
* If the size is 0 or the page requires I/O access, returns NULL; otherwise,
|
|
||||||
* returns the address of the host page similar to tlb_vaddr_to_host().
|
|
||||||
*/
|
|
||||||
void *probe_access(CPUArchState *env, target_ulong addr, int size,
|
|
||||||
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
|
|
||||||
{
|
{
|
||||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||||
target_ulong tlb_addr;
|
target_ulong tlb_addr, page_addr;
|
||||||
size_t elt_ofs;
|
size_t elt_ofs;
|
||||||
int wp_access;
|
int flags;
|
||||||
|
|
||||||
g_assert(-(addr | TARGET_PAGE_MASK) >= size);
|
|
||||||
|
|
||||||
switch (access_type) {
|
switch (access_type) {
|
||||||
case MMU_DATA_LOAD:
|
case MMU_DATA_LOAD:
|
||||||
elt_ofs = offsetof(CPUTLBEntry, addr_read);
|
elt_ofs = offsetof(CPUTLBEntry, addr_read);
|
||||||
wp_access = BP_MEM_READ;
|
|
||||||
break;
|
break;
|
||||||
case MMU_DATA_STORE:
|
case MMU_DATA_STORE:
|
||||||
elt_ofs = offsetof(CPUTLBEntry, addr_write);
|
elt_ofs = offsetof(CPUTLBEntry, addr_write);
|
||||||
wp_access = BP_MEM_WRITE;
|
|
||||||
break;
|
break;
|
||||||
case MMU_INST_FETCH:
|
case MMU_INST_FETCH:
|
||||||
elt_ofs = offsetof(CPUTLBEntry, addr_code);
|
elt_ofs = offsetof(CPUTLBEntry, addr_code);
|
||||||
wp_access = BP_MEM_READ;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
tlb_addr = tlb_read_ofs(entry, elt_ofs);
|
tlb_addr = tlb_read_ofs(entry, elt_ofs);
|
||||||
|
|
||||||
if (unlikely(!tlb_hit(tlb_addr, addr))) {
|
page_addr = addr & TARGET_PAGE_MASK;
|
||||||
if (!victim_tlb_hit(env, mmu_idx, index, elt_ofs,
|
if (!tlb_hit_page(tlb_addr, page_addr)) {
|
||||||
addr & TARGET_PAGE_MASK)) {
|
if (!victim_tlb_hit(env, mmu_idx, index, elt_ofs, page_addr)) {
|
||||||
tlb_fill(env_cpu(env), addr, size, access_type, mmu_idx, retaddr);
|
CPUState *cs = env_cpu(env);
|
||||||
|
CPUClass *cc = CPU_GET_CLASS(cs);
|
||||||
|
|
||||||
|
if (!cc->tlb_fill(cs, addr, fault_size, access_type,
|
||||||
|
mmu_idx, nonfault, retaddr)) {
|
||||||
|
/* Non-faulting page table read failed. */
|
||||||
|
*phost = NULL;
|
||||||
|
return TLB_INVALID_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
/* TLB resize via tlb_fill may have moved the entry. */
|
/* TLB resize via tlb_fill may have moved the entry. */
|
||||||
index = tlb_index(env, mmu_idx, addr);
|
|
||||||
entry = tlb_entry(env, mmu_idx, addr);
|
entry = tlb_entry(env, mmu_idx, addr);
|
||||||
}
|
}
|
||||||
tlb_addr = tlb_read_ofs(entry, elt_ofs);
|
tlb_addr = tlb_read_ofs(entry, elt_ofs);
|
||||||
}
|
}
|
||||||
|
flags = tlb_addr & TLB_FLAGS_MASK;
|
||||||
|
|
||||||
if (!size) {
|
/* Fold all "mmio-like" bits into TLB_MMIO. This is not RAM. */
|
||||||
return NULL;
|
if (unlikely(flags & ~(TLB_WATCHPOINT | TLB_NOTDIRTY))) {
|
||||||
|
*phost = NULL;
|
||||||
|
return TLB_MMIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(tlb_addr & TLB_FLAGS_MASK)) {
|
/* Everything else is RAM. */
|
||||||
|
*phost = (void *)((uintptr_t)addr + entry->addend);
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
int probe_access_flags(CPUArchState *env, target_ulong addr,
|
||||||
|
MMUAccessType access_type, int mmu_idx,
|
||||||
|
bool nonfault, void **phost, uintptr_t retaddr)
|
||||||
|
{
|
||||||
|
int flags;
|
||||||
|
|
||||||
|
flags = probe_access_internal(env, addr, 0, access_type, mmu_idx,
|
||||||
|
nonfault, phost, retaddr);
|
||||||
|
|
||||||
|
/* Handle clean RAM pages. */
|
||||||
|
if (unlikely(flags & TLB_NOTDIRTY)) {
|
||||||
|
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||||
CPUIOTLBEntry *iotlbentry = &env_tlb(env)->d[mmu_idx].iotlb[index];
|
CPUIOTLBEntry *iotlbentry = &env_tlb(env)->d[mmu_idx].iotlb[index];
|
||||||
|
|
||||||
/* Reject I/O access, or other required slow-path. */
|
notdirty_write(env_cpu(env), addr, 1, iotlbentry, retaddr);
|
||||||
if (tlb_addr & (TLB_MMIO | TLB_BSWAP | TLB_DISCARD_WRITE)) {
|
flags &= ~TLB_NOTDIRTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *probe_access(CPUArchState *env, target_ulong addr, int size,
|
||||||
|
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
|
||||||
|
{
|
||||||
|
void *host;
|
||||||
|
int flags;
|
||||||
|
|
||||||
|
g_assert(-(addr | TARGET_PAGE_MASK) >= size);
|
||||||
|
|
||||||
|
flags = probe_access_internal(env, addr, size, access_type, mmu_idx,
|
||||||
|
false, &host, retaddr);
|
||||||
|
|
||||||
|
/* Per the interface, size == 0 merely faults the access. */
|
||||||
|
if (size == 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unlikely(flags & (TLB_NOTDIRTY | TLB_WATCHPOINT))) {
|
||||||
|
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||||
|
CPUIOTLBEntry *iotlbentry = &env_tlb(env)->d[mmu_idx].iotlb[index];
|
||||||
|
|
||||||
/* Handle watchpoints. */
|
/* Handle watchpoints. */
|
||||||
if (tlb_addr & TLB_WATCHPOINT) {
|
if (flags & TLB_WATCHPOINT) {
|
||||||
|
int wp_access = (access_type == MMU_DATA_STORE
|
||||||
|
? BP_MEM_WRITE : BP_MEM_READ);
|
||||||
cpu_check_watchpoint(env_cpu(env), addr, size,
|
cpu_check_watchpoint(env_cpu(env), addr, size,
|
||||||
iotlbentry->attrs, wp_access, retaddr);
|
iotlbentry->attrs, wp_access, retaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle clean RAM pages. */
|
/* Handle clean RAM pages. */
|
||||||
if (tlb_addr & TLB_NOTDIRTY) {
|
if (flags & TLB_NOTDIRTY) {
|
||||||
notdirty_write(env_cpu(env), addr, size, iotlbentry, retaddr);
|
notdirty_write(env_cpu(env), addr, 1, iotlbentry, retaddr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (void *)((uintptr_t)addr + entry->addend);
|
return host;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr,
|
void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr,
|
||||||
MMUAccessType access_type, int mmu_idx)
|
MMUAccessType access_type, int mmu_idx)
|
||||||
{
|
{
|
||||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
void *host;
|
||||||
target_ulong tlb_addr, page;
|
int flags;
|
||||||
size_t elt_ofs;
|
|
||||||
|
|
||||||
switch (access_type) {
|
flags = probe_access_internal(env, addr, 0, access_type,
|
||||||
case MMU_DATA_LOAD:
|
mmu_idx, true, &host, 0);
|
||||||
elt_ofs = offsetof(CPUTLBEntry, addr_read);
|
|
||||||
break;
|
|
||||||
case MMU_DATA_STORE:
|
|
||||||
elt_ofs = offsetof(CPUTLBEntry, addr_write);
|
|
||||||
break;
|
|
||||||
case MMU_INST_FETCH:
|
|
||||||
elt_ofs = offsetof(CPUTLBEntry, addr_code);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
g_assert_not_reached();
|
|
||||||
}
|
|
||||||
|
|
||||||
page = addr & TARGET_PAGE_MASK;
|
/* No combination of flags are expected by the caller. */
|
||||||
tlb_addr = tlb_read_ofs(entry, elt_ofs);
|
return flags ? NULL : host;
|
||||||
|
|
||||||
if (!tlb_hit_page(tlb_addr, page)) {
|
|
||||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
|
||||||
|
|
||||||
if (!victim_tlb_hit(env, mmu_idx, index, elt_ofs, page)) {
|
|
||||||
CPUState *cs = env_cpu(env);
|
|
||||||
CPUClass *cc = CPU_GET_CLASS(cs);
|
|
||||||
|
|
||||||
if (!cc->tlb_fill(cs, addr, 0, access_type, mmu_idx, true, 0)) {
|
|
||||||
/* Non-faulting page table read failed. */
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TLB resize via tlb_fill may have moved the entry. */
|
|
||||||
entry = tlb_entry(env, mmu_idx, addr);
|
|
||||||
}
|
|
||||||
tlb_addr = tlb_read_ofs(entry, elt_ofs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tlb_addr & ~TARGET_PAGE_MASK) {
|
|
||||||
/* IO access */
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (void *)((uintptr_t)addr + entry->addend);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_PLUGIN
|
#ifdef CONFIG_PLUGIN
|
||||||
/*
|
/*
|
||||||
* Perform a TLB lookup and populate the qemu_plugin_hwaddr structure.
|
* Perform a TLB lookup and populate the qemu_plugin_hwaddr structure.
|
||||||
|
@ -190,13 +190,12 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
|
|||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
void *probe_access(CPUArchState *env, target_ulong addr, int size,
|
static int probe_access_internal(CPUArchState *env, target_ulong addr,
|
||||||
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
|
int fault_size, MMUAccessType access_type,
|
||||||
|
bool nonfault, uintptr_t ra)
|
||||||
{
|
{
|
||||||
int flags;
|
int flags;
|
||||||
|
|
||||||
g_assert(-(addr | TARGET_PAGE_MASK) >= size);
|
|
||||||
|
|
||||||
switch (access_type) {
|
switch (access_type) {
|
||||||
case MMU_DATA_STORE:
|
case MMU_DATA_STORE:
|
||||||
flags = PAGE_WRITE;
|
flags = PAGE_WRITE;
|
||||||
@ -212,12 +211,38 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!guest_addr_valid(addr) || page_check_range(addr, 1, flags) < 0) {
|
if (!guest_addr_valid(addr) || page_check_range(addr, 1, flags) < 0) {
|
||||||
|
if (nonfault) {
|
||||||
|
return TLB_INVALID_MASK;
|
||||||
|
} else {
|
||||||
CPUState *cpu = env_cpu(env);
|
CPUState *cpu = env_cpu(env);
|
||||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||||
cc->tlb_fill(cpu, addr, size, access_type, MMU_USER_IDX, false,
|
cc->tlb_fill(cpu, addr, fault_size, access_type,
|
||||||
retaddr);
|
MMU_USER_IDX, false, ra);
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int probe_access_flags(CPUArchState *env, target_ulong addr,
|
||||||
|
MMUAccessType access_type, int mmu_idx,
|
||||||
|
bool nonfault, void **phost, uintptr_t ra)
|
||||||
|
{
|
||||||
|
int flags;
|
||||||
|
|
||||||
|
flags = probe_access_internal(env, addr, 0, access_type, nonfault, ra);
|
||||||
|
*phost = flags ? NULL : g2h(addr);
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *probe_access(CPUArchState *env, target_ulong addr, int size,
|
||||||
|
MMUAccessType access_type, int mmu_idx, uintptr_t ra)
|
||||||
|
{
|
||||||
|
int flags;
|
||||||
|
|
||||||
|
g_assert(-(addr | TARGET_PAGE_MASK) >= size);
|
||||||
|
flags = probe_access_internal(env, addr, size, access_type, false, ra);
|
||||||
|
g_assert(flags == 0);
|
||||||
|
|
||||||
return size ? g2h(addr) : NULL;
|
return size ? g2h(addr) : NULL;
|
||||||
}
|
}
|
||||||
|
@ -328,7 +328,18 @@ CPUArchState *cpu_copy(CPUArchState *env);
|
|||||||
| CPU_INTERRUPT_TGT_EXT_3 \
|
| CPU_INTERRUPT_TGT_EXT_3 \
|
||||||
| CPU_INTERRUPT_TGT_EXT_4)
|
| CPU_INTERRUPT_TGT_EXT_4)
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#ifdef CONFIG_USER_ONLY
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allow some level of source compatibility with softmmu. We do not
|
||||||
|
* support any of the more exotic features, so only invalid pages may
|
||||||
|
* be signaled by probe_access_flags().
|
||||||
|
*/
|
||||||
|
#define TLB_INVALID_MASK (1 << (TARGET_PAGE_BITS_MIN - 1))
|
||||||
|
#define TLB_MMIO 0
|
||||||
|
#define TLB_WATCHPOINT 0
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Flags stored in the low bits of the TLB virtual address.
|
* Flags stored in the low bits of the TLB virtual address.
|
||||||
|
@ -362,6 +362,28 @@ static inline void *probe_read(CPUArchState *env, target_ulong addr, int size,
|
|||||||
return probe_access(env, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr);
|
return probe_access(env, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* probe_access_flags:
|
||||||
|
* @env: CPUArchState
|
||||||
|
* @addr: guest virtual address to look up
|
||||||
|
* @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.
|
||||||
|
*
|
||||||
|
* 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, target_ulong addr,
|
||||||
|
MMUAccessType access_type, int mmu_idx,
|
||||||
|
bool nonfault, void **phost, uintptr_t retaddr);
|
||||||
|
|
||||||
#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */
|
#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */
|
||||||
|
|
||||||
/* Estimated block size for TB allocation. */
|
/* Estimated block size for TB allocation. */
|
||||||
|
Loading…
Reference in New Issue
Block a user