mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/rth/tags/pull-tcg-20181018' into staging
Queued tcg patches. # gpg: Signature made Fri 19 Oct 2018 07:03:20 BST # gpg: using RSA key 64DF38E8AF7E215F # gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" # Primary key fingerprint: 7A48 1E78 868B 4DB6 A85A 05C0 64DF 38E8 AF7E 215F * remotes/rth/tags/pull-tcg-20181018: (21 commits) cputlb: read CPUTLBEntry.addr_write atomically target/s390x: Check HAVE_ATOMIC128 and HAVE_CMPXCHG128 at translate target/s390x: Skip wout, cout helpers if op helper does not return target/s390x: Split do_cdsg, do_lpq, do_stpq target/s390x: Convert to HAVE_CMPXCHG128 and HAVE_ATOMIC128 target/ppc: Convert to HAVE_CMPXCHG128 and HAVE_ATOMIC128 target/arm: Check HAVE_CMPXCHG128 at translate time target/arm: Convert to HAVE_CMPXCHG128 target/i386: Convert to HAVE_CMPXCHG128 tcg: Split CONFIG_ATOMIC128 tcg: Add tlb_index and tlb_entry helpers cputlb: serialize tlb updates with env->tlb_lock cputlb: fix assert_cpu_is_self macro exec: introduce tlb_init target/unicore32: remove tlb_flush from uc32_init_fn target/alpha: remove tlb_flush from alpha_cpu_initfn tcg: distribute tcg_time into TCG contexts tcg: plug holes in struct TCGProfile tcg: fix use of uninitialized variable under CONFIG_PROFILER tcg: access cpu->icount_decr.u16.high with atomics ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -100,19 +100,24 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
DATA_TYPE ret;
|
||||
|
||||
ATOMIC_TRACE_RMW;
|
||||
#if DATA_SIZE == 16
|
||||
ret = atomic16_cmpxchg(haddr, cmpv, newv);
|
||||
#else
|
||||
ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
|
||||
#endif
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if DATA_SIZE >= 16
|
||||
#if HAVE_ATOMIC128
|
||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||
|
||||
ATOMIC_TRACE_LD;
|
||||
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
|
||||
val = atomic16_read(haddr);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return val;
|
||||
}
|
||||
@@ -124,9 +129,10 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
|
||||
ATOMIC_TRACE_ST;
|
||||
__atomic_store(haddr, &val, __ATOMIC_RELAXED);
|
||||
atomic16_set(haddr, val);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
@@ -228,19 +234,24 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
DATA_TYPE ret;
|
||||
|
||||
ATOMIC_TRACE_RMW;
|
||||
#if DATA_SIZE == 16
|
||||
ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv));
|
||||
#else
|
||||
ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
|
||||
#endif
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return BSWAP(ret);
|
||||
}
|
||||
|
||||
#if DATA_SIZE >= 16
|
||||
#if HAVE_ATOMIC128
|
||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||
|
||||
ATOMIC_TRACE_LD;
|
||||
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
|
||||
val = atomic16_read(haddr);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return BSWAP(val);
|
||||
}
|
||||
@@ -253,9 +264,10 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
||||
|
||||
ATOMIC_TRACE_ST;
|
||||
val = BSWAP(val);
|
||||
__atomic_store(haddr, &val, __ATOMIC_RELAXED);
|
||||
atomic16_set(haddr, val);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
|
||||
@@ -416,7 +416,7 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
|
||||
}
|
||||
#endif
|
||||
/* See if we can patch the calling TB. */
|
||||
if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
|
||||
if (last_tb) {
|
||||
tb_add_jump(last_tb, tb_exit, tb);
|
||||
}
|
||||
return tb;
|
||||
|
||||
+127
-108
@@ -32,6 +32,7 @@
|
||||
#include "exec/log.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "qemu/atomic.h"
|
||||
#include "qemu/atomic128.h"
|
||||
|
||||
/* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */
|
||||
/* #define DEBUG_TLB */
|
||||
@@ -58,9 +59,9 @@
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define assert_cpu_is_self(this_cpu) do { \
|
||||
#define assert_cpu_is_self(cpu) do { \
|
||||
if (DEBUG_TLB_GATE) { \
|
||||
g_assert(!cpu->created || qemu_cpu_is_self(cpu)); \
|
||||
g_assert(!(cpu)->created || qemu_cpu_is_self(cpu)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
@@ -73,6 +74,13 @@ QEMU_BUILD_BUG_ON(sizeof(target_ulong) > sizeof(run_on_cpu_data));
|
||||
QEMU_BUILD_BUG_ON(NB_MMU_MODES > 16);
|
||||
#define ALL_MMUIDX_BITS ((1 << NB_MMU_MODES) - 1)
|
||||
|
||||
void tlb_init(CPUState *cpu)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
|
||||
qemu_spin_init(&env->tlb_lock);
|
||||
}
|
||||
|
||||
/* flush_all_helper: run fn across all cpus
|
||||
*
|
||||
* If the wait flag is set then the src cpu's helper will be queued as
|
||||
@@ -125,8 +133,17 @@ static void tlb_flush_nocheck(CPUState *cpu)
|
||||
atomic_set(&env->tlb_flush_count, env->tlb_flush_count + 1);
|
||||
tlb_debug("(count: %zu)\n", tlb_flush_count());
|
||||
|
||||
/*
|
||||
* tlb_table/tlb_v_table updates from any thread must hold tlb_lock.
|
||||
* However, updates from the owner thread (as is the case here; see the
|
||||
* above assert_cpu_is_self) do not need atomic_set because all reads
|
||||
* that do not hold the lock are performed by the same owner thread.
|
||||
*/
|
||||
qemu_spin_lock(&env->tlb_lock);
|
||||
memset(env->tlb_table, -1, sizeof(env->tlb_table));
|
||||
memset(env->tlb_v_table, -1, sizeof(env->tlb_v_table));
|
||||
qemu_spin_unlock(&env->tlb_lock);
|
||||
|
||||
cpu_tb_jmp_cache_clear(cpu);
|
||||
|
||||
env->vtlb_index = 0;
|
||||
@@ -178,6 +195,7 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
|
||||
|
||||
tlb_debug("start: mmu_idx:0x%04lx\n", mmu_idx_bitmask);
|
||||
|
||||
qemu_spin_lock(&env->tlb_lock);
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
|
||||
if (test_bit(mmu_idx, &mmu_idx_bitmask)) {
|
||||
@@ -187,6 +205,7 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
|
||||
memset(env->tlb_v_table[mmu_idx], -1, sizeof(env->tlb_v_table[0]));
|
||||
}
|
||||
}
|
||||
qemu_spin_unlock(&env->tlb_lock);
|
||||
|
||||
cpu_tb_jmp_cache_clear(cpu);
|
||||
|
||||
@@ -239,23 +258,28 @@ static inline bool tlb_hit_page_anyprot(CPUTLBEntry *tlb_entry,
|
||||
target_ulong page)
|
||||
{
|
||||
return tlb_hit_page(tlb_entry->addr_read, page) ||
|
||||
tlb_hit_page(tlb_entry->addr_write, page) ||
|
||||
tlb_hit_page(tlb_addr_write(tlb_entry), page) ||
|
||||
tlb_hit_page(tlb_entry->addr_code, page);
|
||||
}
|
||||
|
||||
static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong page)
|
||||
/* Called with tlb_lock held */
|
||||
static inline void tlb_flush_entry_locked(CPUTLBEntry *tlb_entry,
|
||||
target_ulong page)
|
||||
{
|
||||
if (tlb_hit_page_anyprot(tlb_entry, page)) {
|
||||
memset(tlb_entry, -1, sizeof(*tlb_entry));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void tlb_flush_vtlb_page(CPUArchState *env, int mmu_idx,
|
||||
target_ulong page)
|
||||
/* Called with tlb_lock held */
|
||||
static inline void tlb_flush_vtlb_page_locked(CPUArchState *env, int mmu_idx,
|
||||
target_ulong page)
|
||||
{
|
||||
int k;
|
||||
|
||||
assert_cpu_is_self(ENV_GET_CPU(env));
|
||||
for (k = 0; k < CPU_VTLB_SIZE; k++) {
|
||||
tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], page);
|
||||
tlb_flush_entry_locked(&env->tlb_v_table[mmu_idx][k], page);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +287,6 @@ static void tlb_flush_page_async_work(CPUState *cpu, run_on_cpu_data data)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
target_ulong addr = (target_ulong) data.target_ptr;
|
||||
int i;
|
||||
int mmu_idx;
|
||||
|
||||
assert_cpu_is_self(cpu);
|
||||
@@ -281,11 +304,12 @@ static void tlb_flush_page_async_work(CPUState *cpu, run_on_cpu_data data)
|
||||
}
|
||||
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
qemu_spin_lock(&env->tlb_lock);
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr);
|
||||
tlb_flush_vtlb_page(env, mmu_idx, addr);
|
||||
tlb_flush_entry_locked(tlb_entry(env, mmu_idx, addr), addr);
|
||||
tlb_flush_vtlb_page_locked(env, mmu_idx, addr);
|
||||
}
|
||||
qemu_spin_unlock(&env->tlb_lock);
|
||||
|
||||
tb_flush_jmp_cache(cpu, addr);
|
||||
}
|
||||
@@ -314,20 +338,21 @@ static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu,
|
||||
target_ulong addr_and_mmuidx = (target_ulong) data.target_ptr;
|
||||
target_ulong addr = addr_and_mmuidx & TARGET_PAGE_MASK;
|
||||
unsigned long mmu_idx_bitmap = addr_and_mmuidx & ALL_MMUIDX_BITS;
|
||||
int page = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
int mmu_idx;
|
||||
|
||||
assert_cpu_is_self(cpu);
|
||||
|
||||
tlb_debug("page:%d addr:"TARGET_FMT_lx" mmu_idx:0x%lx\n",
|
||||
page, addr, mmu_idx_bitmap);
|
||||
tlb_debug("flush page addr:"TARGET_FMT_lx" mmu_idx:0x%lx\n",
|
||||
addr, mmu_idx_bitmap);
|
||||
|
||||
qemu_spin_lock(&env->tlb_lock);
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
if (test_bit(mmu_idx, &mmu_idx_bitmap)) {
|
||||
tlb_flush_entry(&env->tlb_table[mmu_idx][page], addr);
|
||||
tlb_flush_vtlb_page(env, mmu_idx, addr);
|
||||
tlb_flush_entry_locked(tlb_entry(env, mmu_idx, addr), addr);
|
||||
tlb_flush_vtlb_page_locked(env, mmu_idx, addr);
|
||||
}
|
||||
}
|
||||
qemu_spin_unlock(&env->tlb_lock);
|
||||
|
||||
tb_flush_jmp_cache(cpu, addr);
|
||||
}
|
||||
@@ -450,72 +475,44 @@ void tlb_unprotect_code(ram_addr_t ram_addr)
|
||||
* most usual is detecting writes to code regions which may invalidate
|
||||
* generated code.
|
||||
*
|
||||
* Because we want other vCPUs to respond to changes straight away we
|
||||
* update the te->addr_write field atomically. If the TLB entry has
|
||||
* been changed by the vCPU in the mean time we skip the update.
|
||||
* Other vCPUs might be reading their TLBs during guest execution, so we update
|
||||
* te->addr_write with atomic_set. We don't need to worry about this for
|
||||
* oversized guests as MTTCG is disabled for them.
|
||||
*
|
||||
* As this function uses atomic accesses we also need to ensure
|
||||
* updates to tlb_entries follow the same access rules. We don't need
|
||||
* to worry about this for oversized guests as MTTCG is disabled for
|
||||
* them.
|
||||
* Called with tlb_lock held.
|
||||
*/
|
||||
|
||||
static void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, uintptr_t start,
|
||||
uintptr_t length)
|
||||
static void tlb_reset_dirty_range_locked(CPUTLBEntry *tlb_entry,
|
||||
uintptr_t start, uintptr_t length)
|
||||
{
|
||||
#if TCG_OVERSIZED_GUEST
|
||||
uintptr_t addr = tlb_entry->addr_write;
|
||||
|
||||
if ((addr & (TLB_INVALID_MASK | TLB_MMIO | TLB_NOTDIRTY)) == 0) {
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
addr += tlb_entry->addend;
|
||||
if ((addr - start) < length) {
|
||||
#if TCG_OVERSIZED_GUEST
|
||||
tlb_entry->addr_write |= TLB_NOTDIRTY;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* paired with atomic_mb_set in tlb_set_page_with_attrs */
|
||||
uintptr_t orig_addr = atomic_mb_read(&tlb_entry->addr_write);
|
||||
uintptr_t addr = orig_addr;
|
||||
|
||||
if ((addr & (TLB_INVALID_MASK | TLB_MMIO | TLB_NOTDIRTY)) == 0) {
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
addr += atomic_read(&tlb_entry->addend);
|
||||
if ((addr - start) < length) {
|
||||
uintptr_t notdirty_addr = orig_addr | TLB_NOTDIRTY;
|
||||
atomic_cmpxchg(&tlb_entry->addr_write, orig_addr, notdirty_addr);
|
||||
atomic_set(&tlb_entry->addr_write,
|
||||
tlb_entry->addr_write | TLB_NOTDIRTY);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* For atomic correctness when running MTTCG we need to use the right
|
||||
* primitives when copying entries */
|
||||
static inline void copy_tlb_helper(CPUTLBEntry *d, CPUTLBEntry *s,
|
||||
bool atomic_set)
|
||||
/*
|
||||
* Called with tlb_lock held.
|
||||
* Called only from the vCPU context, i.e. the TLB's owner thread.
|
||||
*/
|
||||
static inline void copy_tlb_helper_locked(CPUTLBEntry *d, const CPUTLBEntry *s)
|
||||
{
|
||||
#if TCG_OVERSIZED_GUEST
|
||||
*d = *s;
|
||||
#else
|
||||
if (atomic_set) {
|
||||
d->addr_read = s->addr_read;
|
||||
d->addr_code = s->addr_code;
|
||||
atomic_set(&d->addend, atomic_read(&s->addend));
|
||||
/* Pairs with flag setting in tlb_reset_dirty_range */
|
||||
atomic_mb_set(&d->addr_write, atomic_read(&s->addr_write));
|
||||
} else {
|
||||
d->addr_read = s->addr_read;
|
||||
d->addr_write = atomic_read(&s->addr_write);
|
||||
d->addr_code = s->addr_code;
|
||||
d->addend = atomic_read(&s->addend);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* This is a cross vCPU call (i.e. another vCPU resetting the flags of
|
||||
* the target vCPU). As such care needs to be taken that we don't
|
||||
* dangerously race with another vCPU update. The only thing actually
|
||||
* updated is the target TLB entry ->addr_write flags.
|
||||
* the target vCPU).
|
||||
* We must take tlb_lock to avoid racing with another vCPU update. The only
|
||||
* thing actually updated is the target TLB entry ->addr_write flags.
|
||||
*/
|
||||
void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length)
|
||||
{
|
||||
@@ -524,22 +521,26 @@ void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length)
|
||||
int mmu_idx;
|
||||
|
||||
env = cpu->env_ptr;
|
||||
qemu_spin_lock(&env->tlb_lock);
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < CPU_TLB_SIZE; i++) {
|
||||
tlb_reset_dirty_range(&env->tlb_table[mmu_idx][i],
|
||||
start1, length);
|
||||
tlb_reset_dirty_range_locked(&env->tlb_table[mmu_idx][i], start1,
|
||||
length);
|
||||
}
|
||||
|
||||
for (i = 0; i < CPU_VTLB_SIZE; i++) {
|
||||
tlb_reset_dirty_range(&env->tlb_v_table[mmu_idx][i],
|
||||
start1, length);
|
||||
tlb_reset_dirty_range_locked(&env->tlb_v_table[mmu_idx][i], start1,
|
||||
length);
|
||||
}
|
||||
}
|
||||
qemu_spin_unlock(&env->tlb_lock);
|
||||
}
|
||||
|
||||
static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, target_ulong vaddr)
|
||||
/* Called with tlb_lock held */
|
||||
static inline void tlb_set_dirty1_locked(CPUTLBEntry *tlb_entry,
|
||||
target_ulong vaddr)
|
||||
{
|
||||
if (tlb_entry->addr_write == (vaddr | TLB_NOTDIRTY)) {
|
||||
tlb_entry->addr_write = vaddr;
|
||||
@@ -551,23 +552,23 @@ static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, target_ulong vaddr)
|
||||
void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
int i;
|
||||
int mmu_idx;
|
||||
|
||||
assert_cpu_is_self(cpu);
|
||||
|
||||
vaddr &= TARGET_PAGE_MASK;
|
||||
i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
qemu_spin_lock(&env->tlb_lock);
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
tlb_set_dirty1(&env->tlb_table[mmu_idx][i], vaddr);
|
||||
tlb_set_dirty1_locked(tlb_entry(env, mmu_idx, vaddr), vaddr);
|
||||
}
|
||||
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
int k;
|
||||
for (k = 0; k < CPU_VTLB_SIZE; k++) {
|
||||
tlb_set_dirty1(&env->tlb_v_table[mmu_idx][k], vaddr);
|
||||
tlb_set_dirty1_locked(&env->tlb_v_table[mmu_idx][k], vaddr);
|
||||
}
|
||||
}
|
||||
qemu_spin_unlock(&env->tlb_lock);
|
||||
}
|
||||
|
||||
/* Our TLB does not support large pages, so remember the area covered by
|
||||
@@ -654,15 +655,24 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
|
||||
addend = (uintptr_t)memory_region_get_ram_ptr(section->mr) + xlat;
|
||||
}
|
||||
|
||||
/* Make sure there's no cached translation for the new page. */
|
||||
tlb_flush_vtlb_page(env, mmu_idx, vaddr_page);
|
||||
|
||||
code_address = address;
|
||||
iotlb = memory_region_section_get_iotlb(cpu, section, vaddr_page,
|
||||
paddr_page, xlat, prot, &address);
|
||||
|
||||
index = (vaddr_page >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
te = &env->tlb_table[mmu_idx][index];
|
||||
index = tlb_index(env, mmu_idx, vaddr_page);
|
||||
te = tlb_entry(env, mmu_idx, vaddr_page);
|
||||
|
||||
/*
|
||||
* Hold the TLB lock for the rest of the function. We could acquire/release
|
||||
* the lock several times in the function, but it is faster to amortize the
|
||||
* acquisition cost by acquiring it just once. Note that this leads to
|
||||
* a longer critical section, but this is not a concern since the TLB lock
|
||||
* is unlikely to be contended.
|
||||
*/
|
||||
qemu_spin_lock(&env->tlb_lock);
|
||||
|
||||
/* Make sure there's no cached translation for the new page. */
|
||||
tlb_flush_vtlb_page_locked(env, mmu_idx, vaddr_page);
|
||||
|
||||
/*
|
||||
* Only evict the old entry to the victim tlb if it's for a
|
||||
@@ -673,7 +683,7 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
|
||||
CPUTLBEntry *tv = &env->tlb_v_table[mmu_idx][vidx];
|
||||
|
||||
/* Evict the old entry into the victim tlb. */
|
||||
copy_tlb_helper(tv, te, true);
|
||||
copy_tlb_helper_locked(tv, te);
|
||||
env->iotlb_v[mmu_idx][vidx] = env->iotlb[mmu_idx][index];
|
||||
}
|
||||
|
||||
@@ -725,9 +735,8 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
|
||||
}
|
||||
}
|
||||
|
||||
/* Pairs with flag setting in tlb_reset_dirty_range */
|
||||
copy_tlb_helper(te, &tn, true);
|
||||
/* atomic_mb_set(&te->addr_write, write_address); */
|
||||
copy_tlb_helper_locked(te, &tn);
|
||||
qemu_spin_unlock(&env->tlb_lock);
|
||||
}
|
||||
|
||||
/* Add a new TLB entry, but without specifying the memory
|
||||
@@ -773,16 +782,16 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
* repeat the MMU check here. This tlb_fill() call might
|
||||
* longjump out if this access should cause a guest exception.
|
||||
*/
|
||||
int index;
|
||||
CPUTLBEntry *entry;
|
||||
target_ulong tlb_addr;
|
||||
|
||||
tlb_fill(cpu, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr);
|
||||
|
||||
index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
tlb_addr = env->tlb_table[mmu_idx][index].addr_read;
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
tlb_addr = entry->addr_read;
|
||||
if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) {
|
||||
/* RAM access */
|
||||
uintptr_t haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||
uintptr_t haddr = addr + entry->addend;
|
||||
|
||||
return ldn_p((void *)haddr, size);
|
||||
}
|
||||
@@ -840,16 +849,16 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
* repeat the MMU check here. This tlb_fill() call might
|
||||
* longjump out if this access should cause a guest exception.
|
||||
*/
|
||||
int index;
|
||||
CPUTLBEntry *entry;
|
||||
target_ulong tlb_addr;
|
||||
|
||||
tlb_fill(cpu, addr, size, MMU_DATA_STORE, mmu_idx, retaddr);
|
||||
|
||||
index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
tlb_addr = tlb_addr_write(entry);
|
||||
if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) {
|
||||
/* RAM access */
|
||||
uintptr_t haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||
uintptr_t haddr = addr + entry->addend;
|
||||
|
||||
stn_p((void *)haddr, size, val);
|
||||
return;
|
||||
@@ -891,17 +900,28 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
|
||||
size_t elt_ofs, target_ulong page)
|
||||
{
|
||||
size_t vidx;
|
||||
|
||||
assert_cpu_is_self(ENV_GET_CPU(env));
|
||||
for (vidx = 0; vidx < CPU_VTLB_SIZE; ++vidx) {
|
||||
CPUTLBEntry *vtlb = &env->tlb_v_table[mmu_idx][vidx];
|
||||
target_ulong cmp = *(target_ulong *)((uintptr_t)vtlb + elt_ofs);
|
||||
target_ulong cmp;
|
||||
|
||||
/* elt_ofs might correspond to .addr_write, so use atomic_read */
|
||||
#if TCG_OVERSIZED_GUEST
|
||||
cmp = *(target_ulong *)((uintptr_t)vtlb + elt_ofs);
|
||||
#else
|
||||
cmp = atomic_read((target_ulong *)((uintptr_t)vtlb + elt_ofs));
|
||||
#endif
|
||||
|
||||
if (cmp == page) {
|
||||
/* Found entry in victim tlb, swap tlb and iotlb. */
|
||||
CPUTLBEntry tmptlb, *tlb = &env->tlb_table[mmu_idx][index];
|
||||
|
||||
copy_tlb_helper(&tmptlb, tlb, false);
|
||||
copy_tlb_helper(tlb, vtlb, true);
|
||||
copy_tlb_helper(vtlb, &tmptlb, true);
|
||||
qemu_spin_lock(&env->tlb_lock);
|
||||
copy_tlb_helper_locked(&tmptlb, tlb);
|
||||
copy_tlb_helper_locked(tlb, vtlb);
|
||||
copy_tlb_helper_locked(vtlb, &tmptlb);
|
||||
qemu_spin_unlock(&env->tlb_lock);
|
||||
|
||||
CPUIOTLBEntry tmpio, *io = &env->iotlb[mmu_idx][index];
|
||||
CPUIOTLBEntry *vio = &env->iotlb_v[mmu_idx][vidx];
|
||||
@@ -924,20 +944,19 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
|
||||
*/
|
||||
tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
|
||||
{
|
||||
int mmu_idx, index;
|
||||
uintptr_t mmu_idx = cpu_mmu_index(env, true);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
void *p;
|
||||
|
||||
index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
mmu_idx = cpu_mmu_index(env, true);
|
||||
if (unlikely(!tlb_hit(env->tlb_table[mmu_idx][index].addr_code, addr))) {
|
||||
if (unlikely(!tlb_hit(entry->addr_code, addr))) {
|
||||
if (!VICTIM_TLB_HIT(addr_code, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, 0, MMU_INST_FETCH, mmu_idx, 0);
|
||||
}
|
||||
assert(tlb_hit(env->tlb_table[mmu_idx][index].addr_code, addr));
|
||||
assert(tlb_hit(entry->addr_code, addr));
|
||||
}
|
||||
|
||||
if (unlikely(env->tlb_table[mmu_idx][index].addr_code &
|
||||
(TLB_RECHECK | TLB_MMIO))) {
|
||||
if (unlikely(entry->addr_code & (TLB_RECHECK | TLB_MMIO))) {
|
||||
/*
|
||||
* Return -1 if we can't translate and execute from an entire
|
||||
* page of RAM here, which will cause us to execute by loading
|
||||
@@ -949,7 +968,7 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = (void *)((uintptr_t)addr + env->tlb_table[mmu_idx][index].addend);
|
||||
p = (void *)((uintptr_t)addr + entry->addend);
|
||||
return qemu_ram_addr_from_host_nofail(p);
|
||||
}
|
||||
|
||||
@@ -962,10 +981,10 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
|
||||
void probe_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx,
|
||||
uintptr_t retaddr)
|
||||
{
|
||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
|
||||
if (!tlb_hit(tlb_addr, addr)) {
|
||||
if (!tlb_hit(tlb_addr_write(entry), addr)) {
|
||||
/* TLB entry is for a different page */
|
||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, size, MMU_DATA_STORE,
|
||||
@@ -981,9 +1000,9 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
NotDirtyInfo *ndi)
|
||||
{
|
||||
size_t mmu_idx = get_mmuidx(oi);
|
||||
size_t index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
CPUTLBEntry *tlbe = &env->tlb_table[mmu_idx][index];
|
||||
target_ulong tlb_addr = tlbe->addr_write;
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *tlbe = tlb_entry(env, mmu_idx, addr);
|
||||
target_ulong tlb_addr = tlb_addr_write(tlbe);
|
||||
TCGMemOp mop = get_memop(oi);
|
||||
int a_bits = get_alignment_bits(mop);
|
||||
int s_bits = mop & MO_SIZE;
|
||||
@@ -1014,7 +1033,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
tlb_fill(ENV_GET_CPU(env), addr, 1 << s_bits, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
tlb_addr = tlbe->addr_write & ~TLB_INVALID_MASK;
|
||||
tlb_addr = tlb_addr_write(tlbe) & ~TLB_INVALID_MASK;
|
||||
}
|
||||
|
||||
/* Notice an IO access or a needs-MMU-lookup access */
|
||||
@@ -1101,7 +1120,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
#include "atomic_template.h"
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ATOMIC128
|
||||
#if HAVE_CMPXCHG128 || HAVE_ATOMIC128
|
||||
#define DATA_SIZE 16
|
||||
#include "atomic_template.h"
|
||||
#endif
|
||||
|
||||
@@ -111,9 +111,10 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
|
||||
WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
unsigned mmu_idx = get_mmuidx(oi);
|
||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
target_ulong tlb_addr = entry->ADDR_READ;
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
uintptr_t haddr;
|
||||
DATA_TYPE res;
|
||||
@@ -129,7 +130,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
||||
tlb_addr = entry->ADDR_READ;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
@@ -166,7 +167,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||
return res;
|
||||
}
|
||||
|
||||
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||
haddr = addr + entry->addend;
|
||||
#if DATA_SIZE == 1
|
||||
res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr);
|
||||
#else
|
||||
@@ -179,9 +180,10 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||
WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
unsigned mmu_idx = get_mmuidx(oi);
|
||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
target_ulong tlb_addr = entry->ADDR_READ;
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
uintptr_t haddr;
|
||||
DATA_TYPE res;
|
||||
@@ -197,7 +199,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
||||
tlb_addr = entry->ADDR_READ;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
@@ -234,7 +236,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
||||
return res;
|
||||
}
|
||||
|
||||
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||
haddr = addr + entry->addend;
|
||||
res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr);
|
||||
return res;
|
||||
}
|
||||
@@ -275,9 +277,10 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env,
|
||||
void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
unsigned mmu_idx = get_mmuidx(oi);
|
||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
target_ulong tlb_addr = tlb_addr_write(entry);
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
uintptr_t haddr;
|
||||
|
||||
@@ -292,7 +295,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK;
|
||||
tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
@@ -313,16 +316,16 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
if (DATA_SIZE > 1
|
||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
||||
>= TARGET_PAGE_SIZE)) {
|
||||
int i, index2;
|
||||
target_ulong page2, tlb_addr2;
|
||||
int i;
|
||||
target_ulong page2;
|
||||
CPUTLBEntry *entry2;
|
||||
do_unaligned_access:
|
||||
/* Ensure the second page is in the TLB. Note that the first page
|
||||
is already guaranteed to be filled, and that the second page
|
||||
cannot evict the first. */
|
||||
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
||||
index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write;
|
||||
if (!tlb_hit_page(tlb_addr2, page2)
|
||||
entry2 = tlb_entry(env, mmu_idx, page2);
|
||||
if (!tlb_hit_page(tlb_addr_write(entry2), page2)
|
||||
&& !VICTIM_TLB_HIT(addr_write, page2)) {
|
||||
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
@@ -340,7 +343,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
return;
|
||||
}
|
||||
|
||||
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||
haddr = addr + entry->addend;
|
||||
#if DATA_SIZE == 1
|
||||
glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val);
|
||||
#else
|
||||
@@ -352,9 +355,10 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
unsigned mmu_idx = get_mmuidx(oi);
|
||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
target_ulong tlb_addr = tlb_addr_write(entry);
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
uintptr_t haddr;
|
||||
|
||||
@@ -369,7 +373,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK;
|
||||
tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
@@ -390,16 +394,16 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
if (DATA_SIZE > 1
|
||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
||||
>= TARGET_PAGE_SIZE)) {
|
||||
int i, index2;
|
||||
target_ulong page2, tlb_addr2;
|
||||
int i;
|
||||
target_ulong page2;
|
||||
CPUTLBEntry *entry2;
|
||||
do_unaligned_access:
|
||||
/* Ensure the second page is in the TLB. Note that the first page
|
||||
is already guaranteed to be filled, and that the second page
|
||||
cannot evict the first. */
|
||||
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
||||
index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write;
|
||||
if (!tlb_hit_page(tlb_addr2, page2)
|
||||
entry2 = tlb_entry(env, mmu_idx, page2);
|
||||
if (!tlb_hit_page(tlb_addr_write(entry2), page2)
|
||||
&& !VICTIM_TLB_HIT(addr_write, page2)) {
|
||||
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
@@ -417,7 +421,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
return;
|
||||
}
|
||||
|
||||
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||
haddr = addr + entry->addend;
|
||||
glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val);
|
||||
}
|
||||
#endif /* DATA_SIZE > 1 */
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
|
||||
if (!qemu_cpu_is_self(cpu)) {
|
||||
qemu_cpu_kick(cpu);
|
||||
} else {
|
||||
cpu->icount_decr.u16.high = -1;
|
||||
atomic_set(&cpu->icount_decr.u16.high, -1);
|
||||
if (use_icount &&
|
||||
!cpu->can_do_io
|
||||
&& (mask & ~old_mask) != 0) {
|
||||
|
||||
@@ -2341,7 +2341,7 @@ void cpu_interrupt(CPUState *cpu, int mask)
|
||||
{
|
||||
g_assert(qemu_mutex_iothread_locked());
|
||||
cpu->interrupt_request |= mask;
|
||||
cpu->icount_decr.u16.high = -1;
|
||||
atomic_set(&cpu->icount_decr.u16.high, -1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "translate-all.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "qemu/atomic128.h"
|
||||
|
||||
#undef EAX
|
||||
#undef ECX
|
||||
@@ -615,7 +616,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
/* The following is only callable from other helpers, and matches up
|
||||
with the softmmu version. */
|
||||
|
||||
#ifdef CONFIG_ATOMIC128
|
||||
#if HAVE_ATOMIC128 || HAVE_CMPXCHG128
|
||||
|
||||
#undef EXTRA_ARGS
|
||||
#undef ATOMIC_NAME
|
||||
@@ -628,4 +629,4 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
|
||||
#define DATA_SIZE 16
|
||||
#include "atomic_template.h"
|
||||
#endif /* CONFIG_ATOMIC128 */
|
||||
#endif
|
||||
|
||||
@@ -5154,6 +5154,21 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
cmpxchg128=no
|
||||
if test "$int128" = yes -a "$atomic128" = no; then
|
||||
cat > $TMPC << EOF
|
||||
int main(void)
|
||||
{
|
||||
unsigned __int128 x = 0, y = 0;
|
||||
__sync_val_compare_and_swap_16(&x, y, x);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if compile_prog "" "" ; then
|
||||
cmpxchg128=yes
|
||||
fi
|
||||
fi
|
||||
|
||||
#########################################
|
||||
# See if 64-bit atomic operations are supported.
|
||||
# Note that without __atomic builtins, we can only
|
||||
@@ -6663,6 +6678,10 @@ if test "$atomic128" = "yes" ; then
|
||||
echo "CONFIG_ATOMIC128=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$cmpxchg128" = "yes" ; then
|
||||
echo "CONFIG_CMPXCHG128=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$atomic64" = "yes" ; then
|
||||
echo "CONFIG_ATOMIC64=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
@@ -1425,7 +1425,8 @@ static int tcg_cpu_exec(CPUState *cpu)
|
||||
ret = cpu_exec(cpu);
|
||||
cpu_exec_end(cpu);
|
||||
#ifdef CONFIG_PROFILER
|
||||
tcg_time += profile_getclock() - ti;
|
||||
atomic_set(&tcg_ctx->prof.cpu_exec_time,
|
||||
tcg_ctx->prof.cpu_exec_time + profile_getclock() - ti);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -965,6 +965,7 @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp)
|
||||
tcg_target_initialized = true;
|
||||
cc->tcg_initialize();
|
||||
}
|
||||
tlb_init(cpu);
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (qdev_get_vmsd(DEVICE(cpu)) == NULL) {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#endif
|
||||
|
||||
#include "qemu/host-utils.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/queue.h"
|
||||
#ifdef CONFIG_TCG
|
||||
#include "tcg-target.h"
|
||||
@@ -142,6 +143,8 @@ typedef struct CPUIOTLBEntry {
|
||||
|
||||
#define CPU_COMMON_TLB \
|
||||
/* The meaning of the MMU modes is defined in the target code. */ \
|
||||
/* tlb_lock serializes updates to tlb_table and tlb_v_table */ \
|
||||
QemuSpin tlb_lock; \
|
||||
CPUTLBEntry tlb_table[NB_MMU_MODES][CPU_TLB_SIZE]; \
|
||||
CPUTLBEntry tlb_v_table[NB_MMU_MODES][CPU_VTLB_SIZE]; \
|
||||
CPUIOTLBEntry iotlb[NB_MMU_MODES][CPU_TLB_SIZE]; \
|
||||
|
||||
+26
-4
@@ -126,6 +126,29 @@ extern __thread uintptr_t helper_retaddr;
|
||||
/* The memory helpers for tcg-generated code need tcg_target_long etc. */
|
||||
#include "tcg.h"
|
||||
|
||||
static inline target_ulong tlb_addr_write(const CPUTLBEntry *entry)
|
||||
{
|
||||
#if TCG_OVERSIZED_GUEST
|
||||
return entry->addr_write;
|
||||
#else
|
||||
return atomic_read(&entry->addr_write);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Find the TLB index corresponding to the mmu_idx + address pair. */
|
||||
static inline uintptr_t tlb_index(CPUArchState *env, uintptr_t mmu_idx,
|
||||
target_ulong addr)
|
||||
{
|
||||
return (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
}
|
||||
|
||||
/* Find the TLB entry corresponding to the mmu_idx + address pair. */
|
||||
static inline CPUTLBEntry *tlb_entry(CPUArchState *env, uintptr_t mmu_idx,
|
||||
target_ulong addr)
|
||||
{
|
||||
return &env->tlb_table[mmu_idx][tlb_index(env, mmu_idx, addr)];
|
||||
}
|
||||
|
||||
#ifdef MMU_MODE0_SUFFIX
|
||||
#define CPU_MMU_INDEX 0
|
||||
#define MEMSUFFIX MMU_MODE0_SUFFIX
|
||||
@@ -416,8 +439,7 @@ static inline void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr,
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
return g2h(addr);
|
||||
#else
|
||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
CPUTLBEntry *tlbentry = &env->tlb_table[mmu_idx][index];
|
||||
CPUTLBEntry *tlbentry = tlb_entry(env, mmu_idx, addr);
|
||||
abi_ptr tlb_addr;
|
||||
uintptr_t haddr;
|
||||
|
||||
@@ -426,7 +448,7 @@ static inline void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr,
|
||||
tlb_addr = tlbentry->addr_read;
|
||||
break;
|
||||
case 1:
|
||||
tlb_addr = tlbentry->addr_write;
|
||||
tlb_addr = tlb_addr_write(tlbentry);
|
||||
break;
|
||||
case 2:
|
||||
tlb_addr = tlbentry->addr_code;
|
||||
@@ -445,7 +467,7 @@ static inline void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||
haddr = addr + tlbentry->addend;
|
||||
return (void *)haddr;
|
||||
#endif /* defined(CONFIG_USER_ONLY) */
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
|
||||
target_ulong ptr,
|
||||
uintptr_t retaddr)
|
||||
{
|
||||
int page_index;
|
||||
CPUTLBEntry *entry;
|
||||
RES_TYPE res;
|
||||
target_ulong addr;
|
||||
int mmu_idx;
|
||||
@@ -94,15 +94,15 @@ glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
|
||||
#endif
|
||||
|
||||
addr = ptr;
|
||||
page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
mmu_idx = CPU_MMU_INDEX;
|
||||
if (unlikely(env->tlb_table[mmu_idx][page_index].ADDR_READ !=
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
if (unlikely(entry->ADDR_READ !=
|
||||
(addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) {
|
||||
oi = make_memop_idx(SHIFT, mmu_idx);
|
||||
res = glue(glue(helper_ret_ld, URETSUFFIX), MMUSUFFIX)(env, addr,
|
||||
oi, retaddr);
|
||||
} else {
|
||||
uintptr_t hostaddr = addr + env->tlb_table[mmu_idx][page_index].addend;
|
||||
uintptr_t hostaddr = addr + entry->addend;
|
||||
res = glue(glue(ld, USUFFIX), _p)((uint8_t *)hostaddr);
|
||||
}
|
||||
return res;
|
||||
@@ -120,7 +120,8 @@ glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
|
||||
target_ulong ptr,
|
||||
uintptr_t retaddr)
|
||||
{
|
||||
int res, page_index;
|
||||
CPUTLBEntry *entry;
|
||||
int res;
|
||||
target_ulong addr;
|
||||
int mmu_idx;
|
||||
TCGMemOpIdx oi;
|
||||
@@ -132,15 +133,15 @@ glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
|
||||
#endif
|
||||
|
||||
addr = ptr;
|
||||
page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
mmu_idx = CPU_MMU_INDEX;
|
||||
if (unlikely(env->tlb_table[mmu_idx][page_index].ADDR_READ !=
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
if (unlikely(entry->ADDR_READ !=
|
||||
(addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) {
|
||||
oi = make_memop_idx(SHIFT, mmu_idx);
|
||||
res = (DATA_STYPE)glue(glue(helper_ret_ld, SRETSUFFIX),
|
||||
MMUSUFFIX)(env, addr, oi, retaddr);
|
||||
} else {
|
||||
uintptr_t hostaddr = addr + env->tlb_table[mmu_idx][page_index].addend;
|
||||
uintptr_t hostaddr = addr + entry->addend;
|
||||
res = glue(glue(lds, SUFFIX), _p)((uint8_t *)hostaddr);
|
||||
}
|
||||
return res;
|
||||
@@ -162,7 +163,7 @@ glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
|
||||
target_ulong ptr,
|
||||
RES_TYPE v, uintptr_t retaddr)
|
||||
{
|
||||
int page_index;
|
||||
CPUTLBEntry *entry;
|
||||
target_ulong addr;
|
||||
int mmu_idx;
|
||||
TCGMemOpIdx oi;
|
||||
@@ -174,15 +175,15 @@ glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
|
||||
#endif
|
||||
|
||||
addr = ptr;
|
||||
page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
mmu_idx = CPU_MMU_INDEX;
|
||||
if (unlikely(env->tlb_table[mmu_idx][page_index].addr_write !=
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
if (unlikely(tlb_addr_write(entry) !=
|
||||
(addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) {
|
||||
oi = make_memop_idx(SHIFT, mmu_idx);
|
||||
glue(glue(helper_ret_st, SUFFIX), MMUSUFFIX)(env, addr, v, oi,
|
||||
retaddr);
|
||||
} else {
|
||||
uintptr_t hostaddr = addr + env->tlb_table[mmu_idx][page_index].addend;
|
||||
uintptr_t hostaddr = addr + entry->addend;
|
||||
glue(glue(st, SUFFIX), _p)((uint8_t *)hostaddr, v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +99,11 @@ void cpu_address_space_init(CPUState *cpu, int asidx,
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG)
|
||||
/* cputlb.c */
|
||||
/**
|
||||
* tlb_init - initialize a CPU's TLB
|
||||
* @cpu: CPU whose TLB should be initialized
|
||||
*/
|
||||
void tlb_init(CPUState *cpu);
|
||||
/**
|
||||
* tlb_flush_page:
|
||||
* @cpu: CPU whose TLB should be flushed
|
||||
@@ -258,6 +263,9 @@ void tlb_set_page(CPUState *cpu, target_ulong vaddr,
|
||||
void probe_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx,
|
||||
uintptr_t retaddr);
|
||||
#else
|
||||
static inline void tlb_init(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
static inline void tlb_flush_page(CPUState *cpu, target_ulong addr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Simple interface for 128-bit atomic operations.
|
||||
*
|
||||
* Copyright (C) 2018 Linaro, Ltd.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
* See docs/devel/atomics.txt for discussion about the guarantees each
|
||||
* atomic primitive is meant to provide.
|
||||
*/
|
||||
|
||||
#ifndef QEMU_ATOMIC128_H
|
||||
#define QEMU_ATOMIC128_H
|
||||
|
||||
/*
|
||||
* GCC is a house divided about supporting large atomic operations.
|
||||
*
|
||||
* For hosts that only have large compare-and-swap, a legalistic reading
|
||||
* of the C++ standard means that one cannot implement __atomic_read on
|
||||
* read-only memory, and thus all atomic operations must synchronize
|
||||
* through libatomic.
|
||||
*
|
||||
* See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80878
|
||||
*
|
||||
* This interpretation is not especially helpful for QEMU.
|
||||
* For softmmu, all RAM is always read/write from the hypervisor.
|
||||
* For user-only, if the guest doesn't implement such an __atomic_read
|
||||
* then the host need not worry about it either.
|
||||
*
|
||||
* Moreover, using libatomic is not an option, because its interface is
|
||||
* built for std::atomic<T>, and requires that *all* accesses to such an
|
||||
* object go through the library. In our case we do not have an object
|
||||
* in the C/C++ sense, but a view of memory as seen by the guest.
|
||||
* The guest may issue a large atomic operation and then access those
|
||||
* pieces using word-sized accesses. From the hypervisor, we have no
|
||||
* way to connect those two actions.
|
||||
*
|
||||
* Therefore, special case each platform.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_ATOMIC128)
|
||||
static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new)
|
||||
{
|
||||
return atomic_cmpxchg__nocheck(ptr, cmp, new);
|
||||
}
|
||||
# define HAVE_CMPXCHG128 1
|
||||
#elif defined(CONFIG_CMPXCHG128)
|
||||
static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new)
|
||||
{
|
||||
return __sync_val_compare_and_swap_16(ptr, cmp, new);
|
||||
}
|
||||
# define HAVE_CMPXCHG128 1
|
||||
#elif defined(__aarch64__)
|
||||
/* Through gcc 8, aarch64 has no support for 128-bit at all. */
|
||||
static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new)
|
||||
{
|
||||
uint64_t cmpl = int128_getlo(cmp), cmph = int128_gethi(cmp);
|
||||
uint64_t newl = int128_getlo(new), newh = int128_gethi(new);
|
||||
uint64_t oldl, oldh;
|
||||
uint32_t tmp;
|
||||
|
||||
asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t"
|
||||
"cmp %[oldl], %[cmpl]\n\t"
|
||||
"ccmp %[oldh], %[cmph], #0, eq\n\t"
|
||||
"b.ne 1f\n\t"
|
||||
"stlxp %w[tmp], %[newl], %[newh], %[mem]\n\t"
|
||||
"cbnz %w[tmp], 0b\n"
|
||||
"1:"
|
||||
: [mem] "+m"(*ptr), [tmp] "=&r"(tmp),
|
||||
[oldl] "=&r"(oldl), [oldh] "=r"(oldh)
|
||||
: [cmpl] "r"(cmpl), [cmph] "r"(cmph),
|
||||
[newl] "r"(newl), [newh] "r"(newh)
|
||||
: "memory", "cc");
|
||||
|
||||
return int128_make128(oldl, oldh);
|
||||
}
|
||||
# define HAVE_CMPXCHG128 1
|
||||
#else
|
||||
/* Fallback definition that must be optimized away, or error. */
|
||||
Int128 QEMU_ERROR("unsupported atomic")
|
||||
atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new);
|
||||
# define HAVE_CMPXCHG128 0
|
||||
#endif /* Some definition for HAVE_CMPXCHG128 */
|
||||
|
||||
|
||||
#if defined(CONFIG_ATOMIC128)
|
||||
static inline Int128 atomic16_read(Int128 *ptr)
|
||||
{
|
||||
return atomic_read__nocheck(ptr);
|
||||
}
|
||||
|
||||
static inline void atomic16_set(Int128 *ptr, Int128 val)
|
||||
{
|
||||
atomic_set__nocheck(ptr, val);
|
||||
}
|
||||
|
||||
# define HAVE_ATOMIC128 1
|
||||
#elif !defined(CONFIG_USER_ONLY) && defined(__aarch64__)
|
||||
/* We can do better than cmpxchg for AArch64. */
|
||||
static inline Int128 atomic16_read(Int128 *ptr)
|
||||
{
|
||||
uint64_t l, h;
|
||||
uint32_t tmp;
|
||||
|
||||
/* The load must be paired with the store to guarantee not tearing. */
|
||||
asm("0: ldxp %[l], %[h], %[mem]\n\t"
|
||||
"stxp %w[tmp], %[l], %[h], %[mem]\n\t"
|
||||
"cbnz %w[tmp], 0b"
|
||||
: [mem] "+m"(*ptr), [tmp] "=r"(tmp), [l] "=r"(l), [h] "=r"(h));
|
||||
|
||||
return int128_make128(l, h);
|
||||
}
|
||||
|
||||
static inline void atomic16_set(Int128 *ptr, Int128 val)
|
||||
{
|
||||
uint64_t l = int128_getlo(val), h = int128_gethi(val);
|
||||
uint64_t t1, t2;
|
||||
|
||||
/* Load into temporaries to acquire the exclusive access lock. */
|
||||
asm("0: ldxp %[t1], %[t2], %[mem]\n\t"
|
||||
"stxp %w[t1], %[l], %[h], %[mem]\n\t"
|
||||
"cbnz %w[t1], 0b"
|
||||
: [mem] "+m"(*ptr), [t1] "=&r"(t1), [t2] "=&r"(t2)
|
||||
: [l] "r"(l), [h] "r"(h));
|
||||
}
|
||||
|
||||
# define HAVE_ATOMIC128 1
|
||||
#elif !defined(CONFIG_USER_ONLY) && HAVE_CMPXCHG128
|
||||
static inline Int128 atomic16_read(Int128 *ptr)
|
||||
{
|
||||
/* Maybe replace 0 with 0, returning the old value. */
|
||||
return atomic16_cmpxchg(ptr, 0, 0);
|
||||
}
|
||||
|
||||
static inline void atomic16_set(Int128 *ptr, Int128 val)
|
||||
{
|
||||
Int128 old = *ptr, cmp;
|
||||
do {
|
||||
cmp = old;
|
||||
old = atomic16_cmpxchg(ptr, cmp, val);
|
||||
} while (old != cmp);
|
||||
}
|
||||
|
||||
# define HAVE_ATOMIC128 1
|
||||
#else
|
||||
/* Fallback definitions that must be optimized away, or error. */
|
||||
Int128 QEMU_ERROR("unsupported atomic") atomic16_read(Int128 *ptr);
|
||||
void QEMU_ERROR("unsupported atomic") atomic16_set(Int128 *ptr, Int128 val);
|
||||
# define HAVE_ATOMIC128 0
|
||||
#endif /* Some definition for HAVE_ATOMIC128 */
|
||||
|
||||
#endif /* QEMU_ATOMIC128_H */
|
||||
@@ -146,6 +146,17 @@
|
||||
# define QEMU_FLATTEN
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If __attribute__((error)) is present, use it to produce an error at
|
||||
* compile time. Otherwise, one must wait for the linker to diagnose
|
||||
* the missing symbol.
|
||||
*/
|
||||
#if __has_attribute(error)
|
||||
# define QEMU_ERROR(X) __attribute__((error(X)))
|
||||
#else
|
||||
# define QEMU_ERROR(X)
|
||||
#endif
|
||||
|
||||
/* Implement C11 _Generic via GCC builtins. Example:
|
||||
*
|
||||
* QEMU_GENERIC(x, (float, sinf), (long double, sinl), sin) (x)
|
||||
|
||||
@@ -1046,7 +1046,6 @@ static inline int64_t profile_getclock(void)
|
||||
return get_clock();
|
||||
}
|
||||
|
||||
extern int64_t tcg_time;
|
||||
extern int64_t dev_time;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/iothread.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "tcg/tcg.h"
|
||||
|
||||
#if defined(TARGET_S390X)
|
||||
#include "hw/s390x/storage-keys.h"
|
||||
@@ -1966,16 +1967,22 @@ static void hmp_info_numa(Monitor *mon, const QDict *qdict)
|
||||
|
||||
#ifdef CONFIG_PROFILER
|
||||
|
||||
int64_t tcg_time;
|
||||
int64_t dev_time;
|
||||
|
||||
static void hmp_info_profile(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
static int64_t last_cpu_exec_time;
|
||||
int64_t cpu_exec_time;
|
||||
int64_t delta;
|
||||
|
||||
cpu_exec_time = tcg_cpu_exec_time();
|
||||
delta = cpu_exec_time - last_cpu_exec_time;
|
||||
|
||||
monitor_printf(mon, "async time %" PRId64 " (%0.3f)\n",
|
||||
dev_time, dev_time / (double)NANOSECONDS_PER_SECOND);
|
||||
monitor_printf(mon, "qemu time %" PRId64 " (%0.3f)\n",
|
||||
tcg_time, tcg_time / (double)NANOSECONDS_PER_SECOND);
|
||||
tcg_time = 0;
|
||||
delta, delta / (double)NANOSECONDS_PER_SECOND);
|
||||
last_cpu_exec_time = cpu_exec_time;
|
||||
dev_time = 0;
|
||||
}
|
||||
#else
|
||||
|
||||
@@ -265,7 +265,7 @@ static void cpu_common_reset(CPUState *cpu)
|
||||
cpu->mem_io_pc = 0;
|
||||
cpu->mem_io_vaddr = 0;
|
||||
cpu->icount_extra = 0;
|
||||
cpu->icount_decr.u32 = 0;
|
||||
atomic_set(&cpu->icount_decr.u32, 0);
|
||||
cpu->can_do_io = 1;
|
||||
cpu->exception_index = -1;
|
||||
cpu->crash_occurred = false;
|
||||
|
||||
@@ -201,7 +201,6 @@ static void alpha_cpu_initfn(Object *obj)
|
||||
CPUAlphaState *env = &cpu->env;
|
||||
|
||||
cs->env_ptr = env;
|
||||
tlb_flush(cs);
|
||||
|
||||
env->lock_addr = -1;
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user