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-atomic-20161026' into staging
cmpxchg emulation of atomics, v8 # gpg: Signature made Wed 26 Oct 2016 16:30:03 BST # gpg: using RSA key 0xAD1270CC4DD0279B # gpg: Good signature from "Richard Henderson <rth7680@gmail.com>" # gpg: aka "Richard Henderson <rth@redhat.com>" # gpg: aka "Richard Henderson <rth@twiddle.net>" # Primary key fingerprint: 9CB1 8DDA F8E8 49AD 2AFC 16A4 AD12 70CC 4DD0 279B * remotes/rth/tags/pull-atomic-20161026: (37 commits) target-alpha: Emulate LL/SC using cmpxchg helpers target-alpha: Introduce MMU_PHYS_IDX target-arm: remove EXCP_STREX + cpu_exclusive_{test, info} linux-user: remove handling of aarch64's EXCP_STREX linux-user: remove handling of ARM's EXCP_STREX target-arm: emulate aarch64's LL/SC using cmpxchg helpers target-arm: emulate SWP with atomic_xchg helper target-arm: emulate LL/SC using cmpxchg helpers target-arm: Rearrange aa32 load and store functions tests: add atomic_add-bench target-i386: remove helper_lock() target-i386: emulate XCHG using atomic helper target-i386: emulate LOCK'ed BTX ops using atomic helpers target-i386: emulate LOCK'ed XADD using atomic helper target-i386: emulate LOCK'ed NEG using cmpxchg helper target-i386: emulate LOCK'ed NOT using atomic helper target-i386: emulate LOCK'ed INC using atomic helper target-i386: emulate LOCK'ed OP instructions using atomic helpers target-i386: emulate LOCK'ed cmpxchg using cmpxchg helpers tcg: Emit barriers with parallel_cpus ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
+1
-1
@@ -89,7 +89,7 @@ endif
|
||||
|
||||
#######################################################################
|
||||
# Target-independent parts used in system and user emulation
|
||||
common-obj-y += tcg-runtime.o cpus-common.o
|
||||
common-obj-y += cpus-common.o
|
||||
common-obj-y += hw/
|
||||
common-obj-y += qom/
|
||||
common-obj-y += disas/
|
||||
|
||||
@@ -94,6 +94,7 @@ obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o
|
||||
obj-y += fpu/softfloat.o
|
||||
obj-y += target-$(TARGET_BASE_ARCH)/
|
||||
obj-y += disas.o
|
||||
obj-y += tcg-runtime.o
|
||||
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
|
||||
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
|
||||
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Atomic helper templates
|
||||
* Included from tcg-runtime.c and cputlb.c.
|
||||
*
|
||||
* Copyright (c) 2016 Red Hat, Inc
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if DATA_SIZE == 16
|
||||
# define SUFFIX o
|
||||
# define DATA_TYPE Int128
|
||||
# define BSWAP bswap128
|
||||
#elif DATA_SIZE == 8
|
||||
# define SUFFIX q
|
||||
# define DATA_TYPE uint64_t
|
||||
# define BSWAP bswap64
|
||||
#elif DATA_SIZE == 4
|
||||
# define SUFFIX l
|
||||
# define DATA_TYPE uint32_t
|
||||
# define BSWAP bswap32
|
||||
#elif DATA_SIZE == 2
|
||||
# define SUFFIX w
|
||||
# define DATA_TYPE uint16_t
|
||||
# define BSWAP bswap16
|
||||
#elif DATA_SIZE == 1
|
||||
# define SUFFIX b
|
||||
# define DATA_TYPE uint8_t
|
||||
# define BSWAP
|
||||
#else
|
||||
# error unsupported data size
|
||||
#endif
|
||||
|
||||
#if DATA_SIZE >= 4
|
||||
# define ABI_TYPE DATA_TYPE
|
||||
#else
|
||||
# define ABI_TYPE uint32_t
|
||||
#endif
|
||||
|
||||
/* Define host-endian atomic operations. Note that END is used within
|
||||
the ATOMIC_NAME macro, and redefined below. */
|
||||
#if DATA_SIZE == 1
|
||||
# define END
|
||||
#elif defined(HOST_WORDS_BIGENDIAN)
|
||||
# define END _be
|
||||
#else
|
||||
# define END _le
|
||||
#endif
|
||||
|
||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE cmpv, ABI_TYPE newv EXTRA_ARGS)
|
||||
{
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
return atomic_cmpxchg__nocheck(haddr, cmpv, newv);
|
||||
}
|
||||
|
||||
#if DATA_SIZE >= 16
|
||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||
{
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
|
||||
return val;
|
||||
}
|
||||
|
||||
void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
{
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
__atomic_store(haddr, &val, __ATOMIC_RELAXED);
|
||||
}
|
||||
#else
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
{
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
return atomic_xchg__nocheck(haddr, val);
|
||||
}
|
||||
|
||||
#define GEN_ATOMIC_HELPER(X) \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE val EXTRA_ARGS) \
|
||||
{ \
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
return atomic_##X(haddr, val); \
|
||||
} \
|
||||
|
||||
GEN_ATOMIC_HELPER(fetch_add)
|
||||
GEN_ATOMIC_HELPER(fetch_and)
|
||||
GEN_ATOMIC_HELPER(fetch_or)
|
||||
GEN_ATOMIC_HELPER(fetch_xor)
|
||||
GEN_ATOMIC_HELPER(add_fetch)
|
||||
GEN_ATOMIC_HELPER(and_fetch)
|
||||
GEN_ATOMIC_HELPER(or_fetch)
|
||||
GEN_ATOMIC_HELPER(xor_fetch)
|
||||
|
||||
#undef GEN_ATOMIC_HELPER
|
||||
#endif /* DATA SIZE >= 16 */
|
||||
|
||||
#undef END
|
||||
|
||||
#if DATA_SIZE > 1
|
||||
|
||||
/* Define reverse-host-endian atomic operations. Note that END is used
|
||||
within the ATOMIC_NAME macro. */
|
||||
#ifdef HOST_WORDS_BIGENDIAN
|
||||
# define END _le
|
||||
#else
|
||||
# define END _be
|
||||
#endif
|
||||
|
||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE cmpv, ABI_TYPE newv EXTRA_ARGS)
|
||||
{
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
return BSWAP(atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)));
|
||||
}
|
||||
|
||||
#if DATA_SIZE >= 16
|
||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||
{
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
|
||||
return BSWAP(val);
|
||||
}
|
||||
|
||||
void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
{
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
val = BSWAP(val);
|
||||
__atomic_store(haddr, &val, __ATOMIC_RELAXED);
|
||||
}
|
||||
#else
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
{
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
return BSWAP(atomic_xchg__nocheck(haddr, BSWAP(val)));
|
||||
}
|
||||
|
||||
#define GEN_ATOMIC_HELPER(X) \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE val EXTRA_ARGS) \
|
||||
{ \
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
return BSWAP(atomic_##X(haddr, BSWAP(val))); \
|
||||
}
|
||||
|
||||
GEN_ATOMIC_HELPER(fetch_and)
|
||||
GEN_ATOMIC_HELPER(fetch_or)
|
||||
GEN_ATOMIC_HELPER(fetch_xor)
|
||||
GEN_ATOMIC_HELPER(and_fetch)
|
||||
GEN_ATOMIC_HELPER(or_fetch)
|
||||
GEN_ATOMIC_HELPER(xor_fetch)
|
||||
|
||||
#undef GEN_ATOMIC_HELPER
|
||||
|
||||
/* Note that for addition, we need to use a separate cmpxchg loop instead
|
||||
of bswaps for the reverse-host-endian helpers. */
|
||||
ABI_TYPE ATOMIC_NAME(fetch_add)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
{
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE ldo, ldn, ret, sto;
|
||||
|
||||
ldo = atomic_read__nocheck(haddr);
|
||||
while (1) {
|
||||
ret = BSWAP(ldo);
|
||||
sto = BSWAP(ret + val);
|
||||
ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto);
|
||||
if (ldn == ldo) {
|
||||
return ret;
|
||||
}
|
||||
ldo = ldn;
|
||||
}
|
||||
}
|
||||
|
||||
ABI_TYPE ATOMIC_NAME(add_fetch)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
{
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE ldo, ldn, ret, sto;
|
||||
|
||||
ldo = atomic_read__nocheck(haddr);
|
||||
while (1) {
|
||||
ret = BSWAP(ldo) + val;
|
||||
sto = BSWAP(ret);
|
||||
ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto);
|
||||
if (ldn == ldo) {
|
||||
return ret;
|
||||
}
|
||||
ldo = ldn;
|
||||
}
|
||||
}
|
||||
#endif /* DATA_SIZE >= 16 */
|
||||
|
||||
#undef END
|
||||
#endif /* DATA_SIZE > 1 */
|
||||
|
||||
#undef BSWAP
|
||||
#undef ABI_TYPE
|
||||
#undef DATA_TYPE
|
||||
#undef SUFFIX
|
||||
#undef DATA_SIZE
|
||||
@@ -1216,7 +1216,10 @@ case "$cpu" in
|
||||
cc_i386='$(CC) -m32'
|
||||
;;
|
||||
x86_64)
|
||||
CPU_CFLAGS="-m64"
|
||||
# ??? Only extremely old AMD cpus do not have cmpxchg16b.
|
||||
# If we truly care, we should simply detect this case at
|
||||
# runtime and generate the fallback to serial emulation.
|
||||
CPU_CFLAGS="-m64 -mcx16"
|
||||
LDFLAGS="-m64 $LDFLAGS"
|
||||
cc_i386='$(CC) -m32'
|
||||
;;
|
||||
@@ -4521,6 +4524,55 @@ if compile_prog "" "" ; then
|
||||
int128=yes
|
||||
fi
|
||||
|
||||
#########################################
|
||||
# See if 128-bit atomic operations are supported.
|
||||
|
||||
atomic128=no
|
||||
if test "$int128" = "yes"; then
|
||||
cat > $TMPC << EOF
|
||||
int main(void)
|
||||
{
|
||||
unsigned __int128 x = 0, y = 0;
|
||||
y = __atomic_load_16(&x, 0);
|
||||
__atomic_store_16(&x, y, 0);
|
||||
__atomic_compare_exchange_16(&x, &y, x, 0, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if compile_prog "" "" ; then
|
||||
atomic128=yes
|
||||
fi
|
||||
fi
|
||||
|
||||
#########################################
|
||||
# See if 64-bit atomic operations are supported.
|
||||
# Note that without __atomic builtins, we can only
|
||||
# assume atomic loads/stores max at pointer size.
|
||||
|
||||
cat > $TMPC << EOF
|
||||
#include <stdint.h>
|
||||
int main(void)
|
||||
{
|
||||
uint64_t x = 0, y = 0;
|
||||
#ifdef __ATOMIC_RELAXED
|
||||
y = __atomic_load_8(&x, 0);
|
||||
__atomic_store_8(&x, y, 0);
|
||||
__atomic_compare_exchange_8(&x, &y, x, 0, 0, 0);
|
||||
__atomic_exchange_8(&x, y, 0);
|
||||
__atomic_fetch_add_8(&x, y, 0);
|
||||
#else
|
||||
typedef char is_host64[sizeof(void *) >= sizeof(uint64_t) ? 1 : -1];
|
||||
__sync_lock_test_and_set(&x, y);
|
||||
__sync_val_compare_and_swap(&x, y, 0);
|
||||
__sync_fetch_and_add(&x, y);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if compile_prog "" "" ; then
|
||||
atomic64=yes
|
||||
fi
|
||||
|
||||
########################################
|
||||
# check if getauxval is available.
|
||||
|
||||
@@ -5483,6 +5535,14 @@ if test "$int128" = "yes" ; then
|
||||
echo "CONFIG_INT128=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$atomic128" = "yes" ; then
|
||||
echo "CONFIG_ATOMIC128=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$atomic64" = "yes" ; then
|
||||
echo "CONFIG_ATOMIC64=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$getauxval" = "yes" ; then
|
||||
echo "CONFIG_GETAUXVAL=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
@@ -77,3 +77,9 @@ void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc)
|
||||
}
|
||||
siglongjmp(cpu->jmp_env, 1);
|
||||
}
|
||||
|
||||
void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc)
|
||||
{
|
||||
cpu->exception_index = EXCP_ATOMIC;
|
||||
cpu_loop_exit_restore(cpu, pc);
|
||||
}
|
||||
|
||||
+30
@@ -216,6 +216,36 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles,
|
||||
}
|
||||
#endif
|
||||
|
||||
static void cpu_exec_step(CPUState *cpu)
|
||||
{
|
||||
CPUArchState *env = (CPUArchState *)cpu->env_ptr;
|
||||
TranslationBlock *tb;
|
||||
target_ulong cs_base, pc;
|
||||
uint32_t flags;
|
||||
|
||||
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags,
|
||||
1 | CF_NOCACHE | CF_IGNORE_ICOUNT);
|
||||
tb->orig_tb = NULL;
|
||||
/* execute the generated code */
|
||||
trace_exec_tb_nocache(tb, pc);
|
||||
cpu_tb_exec(cpu, tb);
|
||||
tb_phys_invalidate(tb, -1);
|
||||
tb_free(tb);
|
||||
}
|
||||
|
||||
void cpu_exec_step_atomic(CPUState *cpu)
|
||||
{
|
||||
start_exclusive();
|
||||
|
||||
/* Since we got here, we know that parallel_cpus must be true. */
|
||||
parallel_cpus = false;
|
||||
cpu_exec_step(cpu);
|
||||
parallel_cpus = true;
|
||||
|
||||
end_exclusive();
|
||||
}
|
||||
|
||||
struct tb_desc {
|
||||
target_ulong pc;
|
||||
target_ulong cs_base;
|
||||
|
||||
@@ -1497,6 +1497,8 @@ static void tcg_exec_all(void)
|
||||
if (r == EXCP_DEBUG) {
|
||||
cpu_handle_guest_debug(cpu);
|
||||
break;
|
||||
} else if (r == EXCP_ATOMIC) {
|
||||
cpu_exec_step_atomic(cpu);
|
||||
}
|
||||
} else if (cpu->stop || cpu->stopped) {
|
||||
if (cpu->unplug) {
|
||||
|
||||
@@ -23,15 +23,15 @@
|
||||
#include "exec/memory.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
|
||||
#include "exec/cputlb.h"
|
||||
|
||||
#include "exec/memory-internal.h"
|
||||
#include "exec/ram_addr.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "exec/log.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "qemu/atomic.h"
|
||||
|
||||
/* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */
|
||||
/* #define DEBUG_TLB */
|
||||
@@ -498,6 +498,43 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
|
||||
return qemu_ram_addr_from_host_nofail(p);
|
||||
}
|
||||
|
||||
static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
target_ulong addr, uintptr_t retaddr, int size)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
hwaddr physaddr = iotlbentry->addr;
|
||||
MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
|
||||
uint64_t val;
|
||||
|
||||
physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
|
||||
cpu->mem_io_pc = retaddr;
|
||||
if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) {
|
||||
cpu_io_recompile(cpu, retaddr);
|
||||
}
|
||||
|
||||
cpu->mem_io_vaddr = addr;
|
||||
memory_region_dispatch_read(mr, physaddr, &val, size, iotlbentry->attrs);
|
||||
return val;
|
||||
}
|
||||
|
||||
static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
uint64_t val, target_ulong addr,
|
||||
uintptr_t retaddr, int size)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
hwaddr physaddr = iotlbentry->addr;
|
||||
MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
|
||||
|
||||
physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
|
||||
if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) {
|
||||
cpu_io_recompile(cpu, retaddr);
|
||||
}
|
||||
|
||||
cpu->mem_io_vaddr = addr;
|
||||
cpu->mem_io_pc = retaddr;
|
||||
memory_region_dispatch_write(mr, physaddr, val, size, iotlbentry->attrs);
|
||||
}
|
||||
|
||||
/* Return true if ADDR is present in the victim tlb, and has been copied
|
||||
back to the main tlb. */
|
||||
static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
|
||||
@@ -527,34 +564,178 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
|
||||
victim_tlb_hit(env, mmu_idx, index, offsetof(CPUTLBEntry, TY), \
|
||||
(ADDR) & TARGET_PAGE_MASK)
|
||||
|
||||
/* Probe for whether the specified guest write access is permitted.
|
||||
* If it is not permitted then an exception will be taken in the same
|
||||
* way as if this were a real write access (and we will not return).
|
||||
* Otherwise the function will return, and there will be a valid
|
||||
* entry in the TLB for this access.
|
||||
*/
|
||||
void probe_write(CPUArchState *env, target_ulong addr, 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;
|
||||
|
||||
if ((addr & TARGET_PAGE_MASK)
|
||||
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||
/* TLB entry is for a different page */
|
||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, MMU_DATA_STORE, mmu_idx, retaddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Probe for a read-modify-write atomic operation. Do not allow unaligned
|
||||
* operations, or io operations to proceed. Return the host address. */
|
||||
static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
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;
|
||||
TCGMemOp mop = get_memop(oi);
|
||||
int a_bits = get_alignment_bits(mop);
|
||||
int s_bits = mop & MO_SIZE;
|
||||
|
||||
/* Adjust the given return address. */
|
||||
retaddr -= GETPC_ADJ;
|
||||
|
||||
/* Enforce guest required alignment. */
|
||||
if (unlikely(a_bits > 0 && (addr & ((1 << a_bits) - 1)))) {
|
||||
/* ??? Maybe indicate atomic op to cpu_unaligned_access */
|
||||
cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
|
||||
/* Enforce qemu required alignment. */
|
||||
if (unlikely(addr & ((1 << s_bits) - 1))) {
|
||||
/* We get here if guest alignment was not requested,
|
||||
or was not enforced by cpu_unaligned_access above.
|
||||
We might widen the access and emulate, but for now
|
||||
mark an exception and exit the cpu loop. */
|
||||
goto stop_the_world;
|
||||
}
|
||||
|
||||
/* Check TLB entry and enforce page permissions. */
|
||||
if ((addr & TARGET_PAGE_MASK)
|
||||
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, MMU_DATA_STORE, mmu_idx, retaddr);
|
||||
}
|
||||
tlb_addr = tlbe->addr_write;
|
||||
}
|
||||
|
||||
/* Notice an IO access, or a notdirty page. */
|
||||
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||
/* There's really nothing that can be done to
|
||||
support this apart from stop-the-world. */
|
||||
goto stop_the_world;
|
||||
}
|
||||
|
||||
/* Let the guest notice RMW on a write-only page. */
|
||||
if (unlikely(tlbe->addr_read != tlb_addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, 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;
|
||||
}
|
||||
|
||||
return (void *)((uintptr_t)addr + tlbe->addend);
|
||||
|
||||
stop_the_world:
|
||||
cpu_loop_exit_atomic(ENV_GET_CPU(env), retaddr);
|
||||
}
|
||||
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
# define TGT_BE(X) (X)
|
||||
# define TGT_LE(X) BSWAP(X)
|
||||
#else
|
||||
# define TGT_BE(X) BSWAP(X)
|
||||
# define TGT_LE(X) (X)
|
||||
#endif
|
||||
|
||||
#define MMUSUFFIX _mmu
|
||||
|
||||
#define SHIFT 0
|
||||
#define DATA_SIZE 1
|
||||
#include "softmmu_template.h"
|
||||
|
||||
#define SHIFT 1
|
||||
#define DATA_SIZE 2
|
||||
#include "softmmu_template.h"
|
||||
|
||||
#define SHIFT 2
|
||||
#define DATA_SIZE 4
|
||||
#include "softmmu_template.h"
|
||||
|
||||
#define SHIFT 3
|
||||
#define DATA_SIZE 8
|
||||
#include "softmmu_template.h"
|
||||
|
||||
/* First set of helpers allows passing in of OI and RETADDR. This makes
|
||||
them callable from other helpers. */
|
||||
|
||||
#define EXTRA_ARGS , TCGMemOpIdx oi, uintptr_t retaddr
|
||||
#define ATOMIC_NAME(X) \
|
||||
HELPER(glue(glue(glue(atomic_ ## X, SUFFIX), END), _mmu))
|
||||
#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, oi, retaddr)
|
||||
|
||||
#define DATA_SIZE 1
|
||||
#include "atomic_template.h"
|
||||
|
||||
#define DATA_SIZE 2
|
||||
#include "atomic_template.h"
|
||||
|
||||
#define DATA_SIZE 4
|
||||
#include "atomic_template.h"
|
||||
|
||||
#ifdef CONFIG_ATOMIC64
|
||||
#define DATA_SIZE 8
|
||||
#include "atomic_template.h"
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ATOMIC128
|
||||
#define DATA_SIZE 16
|
||||
#include "atomic_template.h"
|
||||
#endif
|
||||
|
||||
/* Second set of helpers are directly callable from TCG as helpers. */
|
||||
|
||||
#undef EXTRA_ARGS
|
||||
#undef ATOMIC_NAME
|
||||
#undef ATOMIC_MMU_LOOKUP
|
||||
#define EXTRA_ARGS , TCGMemOpIdx oi
|
||||
#define ATOMIC_NAME(X) HELPER(glue(glue(atomic_ ## X, SUFFIX), END))
|
||||
#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, oi, GETPC())
|
||||
|
||||
#define DATA_SIZE 1
|
||||
#include "atomic_template.h"
|
||||
|
||||
#define DATA_SIZE 2
|
||||
#include "atomic_template.h"
|
||||
|
||||
#define DATA_SIZE 4
|
||||
#include "atomic_template.h"
|
||||
|
||||
#ifdef CONFIG_ATOMIC64
|
||||
#define DATA_SIZE 8
|
||||
#include "atomic_template.h"
|
||||
#endif
|
||||
|
||||
/* Code access functions. */
|
||||
|
||||
#undef MMUSUFFIX
|
||||
|
||||
#define MMUSUFFIX _cmmu
|
||||
#undef GETPC
|
||||
#define GETPC() ((uintptr_t)0)
|
||||
#define SOFTMMU_CODE_ACCESS
|
||||
|
||||
#define SHIFT 0
|
||||
#define DATA_SIZE 1
|
||||
#include "softmmu_template.h"
|
||||
|
||||
#define SHIFT 1
|
||||
#define DATA_SIZE 2
|
||||
#include "softmmu_template.h"
|
||||
|
||||
#define SHIFT 2
|
||||
#define DATA_SIZE 4
|
||||
#include "softmmu_template.h"
|
||||
|
||||
#define SHIFT 3
|
||||
#define DATA_SIZE 8
|
||||
#include "softmmu_template.h"
|
||||
|
||||
@@ -352,9 +352,9 @@ static inline bool section_covers_addr(const MemoryRegionSection *section,
|
||||
/* Memory topology clips a memory region to [0, 2^64); size.hi > 0 means
|
||||
* the section must cover the entire address space.
|
||||
*/
|
||||
return section->size.hi ||
|
||||
return int128_gethi(section->size) ||
|
||||
range_covers_byte(section->offset_within_address_space,
|
||||
section->size.lo, addr);
|
||||
int128_getlo(section->size), addr);
|
||||
}
|
||||
|
||||
static MemoryRegionSection *phys_page_find(PhysPageEntry lp, hwaddr addr,
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#define EXCP_DEBUG 0x10002 /* cpu stopped after a breakpoint or singlestep */
|
||||
#define EXCP_HALTED 0x10003 /* cpu is halted (waiting for external event) */
|
||||
#define EXCP_YIELD 0x10004 /* cpu wants to yield timeslice to another */
|
||||
#define EXCP_ATOMIC 0x10005 /* stop-the-world and emulate atomic */
|
||||
|
||||
/* some important defines:
|
||||
*
|
||||
|
||||
@@ -59,6 +59,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
|
||||
void QEMU_NORETURN cpu_loop_exit(CPUState *cpu);
|
||||
void QEMU_NORETURN cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc);
|
||||
void QEMU_NORETURN cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc);
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
void cpu_reloading_memory_map(void);
|
||||
|
||||
@@ -80,6 +80,7 @@ void tcg_exec_init(unsigned long tb_size);
|
||||
bool tcg_enabled(void);
|
||||
|
||||
void cpu_exec_init_all(void);
|
||||
void cpu_exec_step_atomic(CPUState *cpu);
|
||||
|
||||
/**
|
||||
* set_preferred_target_page_bits:
|
||||
|
||||
+53
-14
@@ -99,15 +99,21 @@
|
||||
* no effect on the generated code but not using the atomic primitives
|
||||
* will get flagged by sanitizers as a violation.
|
||||
*/
|
||||
#define atomic_read__nocheck(ptr) \
|
||||
__atomic_load_n(ptr, __ATOMIC_RELAXED)
|
||||
|
||||
#define atomic_read(ptr) \
|
||||
({ \
|
||||
QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \
|
||||
__atomic_load_n(ptr, __ATOMIC_RELAXED); \
|
||||
atomic_read__nocheck(ptr); \
|
||||
})
|
||||
|
||||
#define atomic_set__nocheck(ptr, i) \
|
||||
__atomic_store_n(ptr, i, __ATOMIC_RELAXED)
|
||||
|
||||
#define atomic_set(ptr, i) do { \
|
||||
QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \
|
||||
__atomic_store_n(ptr, i, __ATOMIC_RELAXED); \
|
||||
atomic_set__nocheck(ptr, i); \
|
||||
} while(0)
|
||||
|
||||
/* See above: most compilers currently treat consume and acquire the
|
||||
@@ -151,20 +157,27 @@
|
||||
|
||||
/* All the remaining operations are fully sequentially consistent */
|
||||
|
||||
#define atomic_xchg__nocheck(ptr, i) ({ \
|
||||
__atomic_exchange_n(ptr, (i), __ATOMIC_SEQ_CST); \
|
||||
})
|
||||
|
||||
#define atomic_xchg(ptr, i) ({ \
|
||||
QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \
|
||||
__atomic_exchange_n(ptr, i, __ATOMIC_SEQ_CST); \
|
||||
atomic_xchg__nocheck(ptr, i); \
|
||||
})
|
||||
|
||||
/* Returns the eventual value, failed or not */
|
||||
#define atomic_cmpxchg(ptr, old, new) \
|
||||
({ \
|
||||
QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \
|
||||
#define atomic_cmpxchg__nocheck(ptr, old, new) ({ \
|
||||
typeof_strip_qual(*ptr) _old = (old); \
|
||||
__atomic_compare_exchange_n(ptr, &_old, new, false, \
|
||||
__ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); \
|
||||
_old; \
|
||||
})
|
||||
})
|
||||
|
||||
#define atomic_cmpxchg(ptr, old, new) ({ \
|
||||
QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \
|
||||
atomic_cmpxchg__nocheck(ptr, old, new); \
|
||||
})
|
||||
|
||||
/* Provide shorter names for GCC atomic builtins, return old value */
|
||||
#define atomic_fetch_inc(ptr) __atomic_fetch_add(ptr, 1, __ATOMIC_SEQ_CST)
|
||||
@@ -173,6 +186,15 @@
|
||||
#define atomic_fetch_sub(ptr, n) __atomic_fetch_sub(ptr, n, __ATOMIC_SEQ_CST)
|
||||
#define atomic_fetch_and(ptr, n) __atomic_fetch_and(ptr, n, __ATOMIC_SEQ_CST)
|
||||
#define atomic_fetch_or(ptr, n) __atomic_fetch_or(ptr, n, __ATOMIC_SEQ_CST)
|
||||
#define atomic_fetch_xor(ptr, n) __atomic_fetch_xor(ptr, n, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define atomic_inc_fetch(ptr) __atomic_add_fetch(ptr, 1, __ATOMIC_SEQ_CST)
|
||||
#define atomic_dec_fetch(ptr) __atomic_sub_fetch(ptr, 1, __ATOMIC_SEQ_CST)
|
||||
#define atomic_add_fetch(ptr, n) __atomic_add_fetch(ptr, n, __ATOMIC_SEQ_CST)
|
||||
#define atomic_sub_fetch(ptr, n) __atomic_sub_fetch(ptr, n, __ATOMIC_SEQ_CST)
|
||||
#define atomic_and_fetch(ptr, n) __atomic_and_fetch(ptr, n, __ATOMIC_SEQ_CST)
|
||||
#define atomic_or_fetch(ptr, n) __atomic_or_fetch(ptr, n, __ATOMIC_SEQ_CST)
|
||||
#define atomic_xor_fetch(ptr, n) __atomic_xor_fetch(ptr, n, __ATOMIC_SEQ_CST)
|
||||
|
||||
/* And even shorter names that return void. */
|
||||
#define atomic_inc(ptr) ((void) __atomic_fetch_add(ptr, 1, __ATOMIC_SEQ_CST))
|
||||
@@ -181,6 +203,7 @@
|
||||
#define atomic_sub(ptr, n) ((void) __atomic_fetch_sub(ptr, n, __ATOMIC_SEQ_CST))
|
||||
#define atomic_and(ptr, n) ((void) __atomic_fetch_and(ptr, n, __ATOMIC_SEQ_CST))
|
||||
#define atomic_or(ptr, n) ((void) __atomic_fetch_or(ptr, n, __ATOMIC_SEQ_CST))
|
||||
#define atomic_xor(ptr, n) ((void) __atomic_fetch_xor(ptr, n, __ATOMIC_SEQ_CST))
|
||||
|
||||
#else /* __ATOMIC_RELAXED */
|
||||
|
||||
@@ -269,8 +292,11 @@
|
||||
/* These will only be atomic if the processor does the fetch or store
|
||||
* in a single issue memory operation
|
||||
*/
|
||||
#define atomic_read(ptr) (*(__typeof__(*ptr) volatile*) (ptr))
|
||||
#define atomic_set(ptr, i) ((*(__typeof__(*ptr) volatile*) (ptr)) = (i))
|
||||
#define atomic_read__nocheck(p) (*(__typeof__(*(p)) volatile*) (p))
|
||||
#define atomic_set__nocheck(p, i) ((*(__typeof__(*(p)) volatile*) (p)) = (i))
|
||||
|
||||
#define atomic_read(ptr) atomic_read__nocheck(ptr)
|
||||
#define atomic_set(ptr, i) atomic_set__nocheck(ptr,i)
|
||||
|
||||
/**
|
||||
* atomic_rcu_read - reads a RCU-protected pointer to a local variable
|
||||
@@ -331,15 +357,27 @@
|
||||
#define atomic_xchg(ptr, i) (smp_mb(), __sync_lock_test_and_set(ptr, i))
|
||||
#endif
|
||||
#endif
|
||||
#define atomic_xchg__nocheck atomic_xchg
|
||||
|
||||
/* Provide shorter names for GCC atomic builtins. */
|
||||
#define atomic_fetch_inc(ptr) __sync_fetch_and_add(ptr, 1)
|
||||
#define atomic_fetch_dec(ptr) __sync_fetch_and_add(ptr, -1)
|
||||
#define atomic_fetch_add __sync_fetch_and_add
|
||||
#define atomic_fetch_sub __sync_fetch_and_sub
|
||||
#define atomic_fetch_and __sync_fetch_and_and
|
||||
#define atomic_fetch_or __sync_fetch_and_or
|
||||
#define atomic_cmpxchg __sync_val_compare_and_swap
|
||||
#define atomic_fetch_add(ptr, n) __sync_fetch_and_add(ptr, n)
|
||||
#define atomic_fetch_sub(ptr, n) __sync_fetch_and_sub(ptr, n)
|
||||
#define atomic_fetch_and(ptr, n) __sync_fetch_and_and(ptr, n)
|
||||
#define atomic_fetch_or(ptr, n) __sync_fetch_and_or(ptr, n)
|
||||
#define atomic_fetch_xor(ptr, n) __sync_fetch_and_xor(ptr, n)
|
||||
|
||||
#define atomic_inc_fetch(ptr) __sync_add_and_fetch(ptr, 1)
|
||||
#define atomic_dec_fetch(ptr) __sync_add_and_fetch(ptr, -1)
|
||||
#define atomic_add_fetch(ptr, n) __sync_add_and_fetch(ptr, n)
|
||||
#define atomic_sub_fetch(ptr, n) __sync_sub_and_fetch(ptr, n)
|
||||
#define atomic_and_fetch(ptr, n) __sync_and_and_fetch(ptr, n)
|
||||
#define atomic_or_fetch(ptr, n) __sync_or_and_fetch(ptr, n)
|
||||
#define atomic_xor_fetch(ptr, n) __sync_xor_and_fetch(ptr, n)
|
||||
|
||||
#define atomic_cmpxchg(ptr, old, new) __sync_val_compare_and_swap(ptr, old, new)
|
||||
#define atomic_cmpxchg__nocheck(ptr, old, new) atomic_cmpxchg(ptr, old, new)
|
||||
|
||||
/* And even shorter names that return void. */
|
||||
#define atomic_inc(ptr) ((void) __sync_fetch_and_add(ptr, 1))
|
||||
@@ -348,6 +386,7 @@
|
||||
#define atomic_sub(ptr, n) ((void) __sync_fetch_and_sub(ptr, n))
|
||||
#define atomic_and(ptr, n) ((void) __sync_fetch_and_and(ptr, n))
|
||||
#define atomic_or(ptr, n) ((void) __sync_fetch_and_or(ptr, n))
|
||||
#define atomic_xor(ptr, n) ((void) __sync_fetch_and_xor(ptr, n))
|
||||
|
||||
#endif /* __ATOMIC_RELAXED */
|
||||
|
||||
|
||||
+165
-6
@@ -1,6 +1,149 @@
|
||||
#ifndef INT128_H
|
||||
#define INT128_H
|
||||
|
||||
#ifdef CONFIG_INT128
|
||||
#include "qemu/bswap.h"
|
||||
|
||||
typedef __int128_t Int128;
|
||||
|
||||
static inline Int128 int128_make64(uint64_t a)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
static inline Int128 int128_make128(uint64_t lo, uint64_t hi)
|
||||
{
|
||||
return (__uint128_t)hi << 64 | lo;
|
||||
}
|
||||
|
||||
static inline uint64_t int128_get64(Int128 a)
|
||||
{
|
||||
uint64_t r = a;
|
||||
assert(r == a);
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline uint64_t int128_getlo(Int128 a)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
static inline int64_t int128_gethi(Int128 a)
|
||||
{
|
||||
return a >> 64;
|
||||
}
|
||||
|
||||
static inline Int128 int128_zero(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline Int128 int128_one(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline Int128 int128_2_64(void)
|
||||
{
|
||||
return (Int128)1 << 64;
|
||||
}
|
||||
|
||||
static inline Int128 int128_exts64(int64_t a)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
static inline Int128 int128_and(Int128 a, Int128 b)
|
||||
{
|
||||
return a & b;
|
||||
}
|
||||
|
||||
static inline Int128 int128_rshift(Int128 a, int n)
|
||||
{
|
||||
return a >> n;
|
||||
}
|
||||
|
||||
static inline Int128 int128_add(Int128 a, Int128 b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
|
||||
static inline Int128 int128_neg(Int128 a)
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
static inline Int128 int128_sub(Int128 a, Int128 b)
|
||||
{
|
||||
return a - b;
|
||||
}
|
||||
|
||||
static inline bool int128_nonneg(Int128 a)
|
||||
{
|
||||
return a >= 0;
|
||||
}
|
||||
|
||||
static inline bool int128_eq(Int128 a, Int128 b)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
|
||||
static inline bool int128_ne(Int128 a, Int128 b)
|
||||
{
|
||||
return a != b;
|
||||
}
|
||||
|
||||
static inline bool int128_ge(Int128 a, Int128 b)
|
||||
{
|
||||
return a >= b;
|
||||
}
|
||||
|
||||
static inline bool int128_lt(Int128 a, Int128 b)
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
|
||||
static inline bool int128_le(Int128 a, Int128 b)
|
||||
{
|
||||
return a <= b;
|
||||
}
|
||||
|
||||
static inline bool int128_gt(Int128 a, Int128 b)
|
||||
{
|
||||
return a > b;
|
||||
}
|
||||
|
||||
static inline bool int128_nz(Int128 a)
|
||||
{
|
||||
return a != 0;
|
||||
}
|
||||
|
||||
static inline Int128 int128_min(Int128 a, Int128 b)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
static inline Int128 int128_max(Int128 a, Int128 b)
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
static inline void int128_addto(Int128 *a, Int128 b)
|
||||
{
|
||||
*a += b;
|
||||
}
|
||||
|
||||
static inline void int128_subfrom(Int128 *a, Int128 b)
|
||||
{
|
||||
*a -= b;
|
||||
}
|
||||
|
||||
static inline Int128 bswap128(Int128 a)
|
||||
{
|
||||
return int128_make128(bswap64(int128_gethi(a)), bswap64(int128_getlo(a)));
|
||||
}
|
||||
|
||||
#else /* !CONFIG_INT128 */
|
||||
|
||||
typedef struct Int128 Int128;
|
||||
|
||||
@@ -14,12 +157,27 @@ static inline Int128 int128_make64(uint64_t a)
|
||||
return (Int128) { a, 0 };
|
||||
}
|
||||
|
||||
static inline Int128 int128_make128(uint64_t lo, uint64_t hi)
|
||||
{
|
||||
return (Int128) { lo, hi };
|
||||
}
|
||||
|
||||
static inline uint64_t int128_get64(Int128 a)
|
||||
{
|
||||
assert(!a.hi);
|
||||
return a.lo;
|
||||
}
|
||||
|
||||
static inline uint64_t int128_getlo(Int128 a)
|
||||
{
|
||||
return a.lo;
|
||||
}
|
||||
|
||||
static inline int64_t int128_gethi(Int128 a)
|
||||
{
|
||||
return a.hi;
|
||||
}
|
||||
|
||||
static inline Int128 int128_zero(void)
|
||||
{
|
||||
return int128_make64(0);
|
||||
@@ -53,9 +211,9 @@ static inline Int128 int128_rshift(Int128 a, int n)
|
||||
}
|
||||
h = a.hi >> (n & 63);
|
||||
if (n >= 64) {
|
||||
return (Int128) { h, h >> 63 };
|
||||
return int128_make128(h, h >> 63);
|
||||
} else {
|
||||
return (Int128) { (a.lo >> n) | ((uint64_t)a.hi << (64 - n)), h };
|
||||
return int128_make128((a.lo >> n) | ((uint64_t)a.hi << (64 - n)), h);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,18 +227,18 @@ static inline Int128 int128_add(Int128 a, Int128 b)
|
||||
*
|
||||
* So the carry is lo < a.lo.
|
||||
*/
|
||||
return (Int128) { lo, (uint64_t)a.hi + b.hi + (lo < a.lo) };
|
||||
return int128_make128(lo, (uint64_t)a.hi + b.hi + (lo < a.lo));
|
||||
}
|
||||
|
||||
static inline Int128 int128_neg(Int128 a)
|
||||
{
|
||||
uint64_t lo = -a.lo;
|
||||
return (Int128) { lo, ~(uint64_t)a.hi + !lo };
|
||||
return int128_make128(lo, ~(uint64_t)a.hi + !lo);
|
||||
}
|
||||
|
||||
static inline Int128 int128_sub(Int128 a, Int128 b)
|
||||
{
|
||||
return (Int128){ a.lo - b.lo, (uint64_t)a.hi - b.hi - (a.lo < b.lo) };
|
||||
return int128_make128(a.lo - b.lo, (uint64_t)a.hi - b.hi - (a.lo < b.lo));
|
||||
}
|
||||
|
||||
static inline bool int128_nonneg(Int128 a)
|
||||
@@ -143,4 +301,5 @@ static inline void int128_subfrom(Int128 *a, Int128 b)
|
||||
*a = int128_sub(*a, b);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* CONFIG_INT128 */
|
||||
#endif /* INT128_H */
|
||||
|
||||
+45
-267
@@ -354,6 +354,9 @@ void cpu_loop(CPUX86State *env)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXCP_ATOMIC:
|
||||
cpu_exec_step_atomic(cs);
|
||||
break;
|
||||
default:
|
||||
pc = env->segs[R_CS].base + env->eip;
|
||||
EXCP_DUMP(env, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n",
|
||||
@@ -550,94 +553,6 @@ do_kernel_trap(CPUARMState *env)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Store exclusive handling for AArch32 */
|
||||
static int do_strex(CPUARMState *env)
|
||||
{
|
||||
uint64_t val;
|
||||
int size;
|
||||
int rc = 1;
|
||||
int segv = 0;
|
||||
uint32_t addr;
|
||||
start_exclusive();
|
||||
if (env->exclusive_addr != env->exclusive_test) {
|
||||
goto fail;
|
||||
}
|
||||
/* We know we're always AArch32 so the address is in uint32_t range
|
||||
* unless it was the -1 exclusive-monitor-lost value (which won't
|
||||
* match exclusive_test above).
|
||||
*/
|
||||
assert(extract64(env->exclusive_addr, 32, 32) == 0);
|
||||
addr = env->exclusive_addr;
|
||||
size = env->exclusive_info & 0xf;
|
||||
switch (size) {
|
||||
case 0:
|
||||
segv = get_user_u8(val, addr);
|
||||
break;
|
||||
case 1:
|
||||
segv = get_user_data_u16(val, addr, env);
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
segv = get_user_data_u32(val, addr, env);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
if (segv) {
|
||||
env->exception.vaddress = addr;
|
||||
goto done;
|
||||
}
|
||||
if (size == 3) {
|
||||
uint32_t valhi;
|
||||
segv = get_user_data_u32(valhi, addr + 4, env);
|
||||
if (segv) {
|
||||
env->exception.vaddress = addr + 4;
|
||||
goto done;
|
||||
}
|
||||
if (arm_cpu_bswap_data(env)) {
|
||||
val = deposit64((uint64_t)valhi, 32, 32, val);
|
||||
} else {
|
||||
val = deposit64(val, 32, 32, valhi);
|
||||
}
|
||||
}
|
||||
if (val != env->exclusive_val) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
val = env->regs[(env->exclusive_info >> 8) & 0xf];
|
||||
switch (size) {
|
||||
case 0:
|
||||
segv = put_user_u8(val, addr);
|
||||
break;
|
||||
case 1:
|
||||
segv = put_user_data_u16(val, addr, env);
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
segv = put_user_data_u32(val, addr, env);
|
||||
break;
|
||||
}
|
||||
if (segv) {
|
||||
env->exception.vaddress = addr;
|
||||
goto done;
|
||||
}
|
||||
if (size == 3) {
|
||||
val = env->regs[(env->exclusive_info >> 12) & 0xf];
|
||||
segv = put_user_data_u32(val, addr + 4, env);
|
||||
if (segv) {
|
||||
env->exception.vaddress = addr + 4;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
rc = 0;
|
||||
fail:
|
||||
env->regs[15] += 4;
|
||||
env->regs[(env->exclusive_info >> 4) & 0xf] = rc;
|
||||
done:
|
||||
end_exclusive();
|
||||
return segv;
|
||||
}
|
||||
|
||||
void cpu_loop(CPUARMState *env)
|
||||
{
|
||||
CPUState *cs = CPU(arm_env_get_cpu(env));
|
||||
@@ -812,11 +727,6 @@ void cpu_loop(CPUARMState *env)
|
||||
case EXCP_INTERRUPT:
|
||||
/* just indicate that signals should be handled asap */
|
||||
break;
|
||||
case EXCP_STREX:
|
||||
if (!do_strex(env)) {
|
||||
break;
|
||||
}
|
||||
/* fall through for segv */
|
||||
case EXCP_PREFETCH_ABORT:
|
||||
case EXCP_DATA_ABORT:
|
||||
addr = env->exception.vaddress;
|
||||
@@ -851,6 +761,9 @@ void cpu_loop(CPUARMState *env)
|
||||
case EXCP_YIELD:
|
||||
/* nothing to do here for user-mode, just resume guest code */
|
||||
break;
|
||||
case EXCP_ATOMIC:
|
||||
cpu_exec_step_atomic(cs);
|
||||
break;
|
||||
default:
|
||||
error:
|
||||
EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr);
|
||||
@@ -862,124 +775,6 @@ void cpu_loop(CPUARMState *env)
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Handle AArch64 store-release exclusive
|
||||
*
|
||||
* rs = gets the status result of store exclusive
|
||||
* rt = is the register that is stored
|
||||
* rt2 = is the second register store (in STP)
|
||||
*
|
||||
*/
|
||||
static int do_strex_a64(CPUARMState *env)
|
||||
{
|
||||
uint64_t val;
|
||||
int size;
|
||||
bool is_pair;
|
||||
int rc = 1;
|
||||
int segv = 0;
|
||||
uint64_t addr;
|
||||
int rs, rt, rt2;
|
||||
|
||||
start_exclusive();
|
||||
/* size | is_pair << 2 | (rs << 4) | (rt << 9) | (rt2 << 14)); */
|
||||
size = extract32(env->exclusive_info, 0, 2);
|
||||
is_pair = extract32(env->exclusive_info, 2, 1);
|
||||
rs = extract32(env->exclusive_info, 4, 5);
|
||||
rt = extract32(env->exclusive_info, 9, 5);
|
||||
rt2 = extract32(env->exclusive_info, 14, 5);
|
||||
|
||||
addr = env->exclusive_addr;
|
||||
|
||||
if (addr != env->exclusive_test) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
switch (size) {
|
||||
case 0:
|
||||
segv = get_user_u8(val, addr);
|
||||
break;
|
||||
case 1:
|
||||
segv = get_user_u16(val, addr);
|
||||
break;
|
||||
case 2:
|
||||
segv = get_user_u32(val, addr);
|
||||
break;
|
||||
case 3:
|
||||
segv = get_user_u64(val, addr);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
if (segv) {
|
||||
env->exception.vaddress = addr;
|
||||
goto error;
|
||||
}
|
||||
if (val != env->exclusive_val) {
|
||||
goto finish;
|
||||
}
|
||||
if (is_pair) {
|
||||
if (size == 2) {
|
||||
segv = get_user_u32(val, addr + 4);
|
||||
} else {
|
||||
segv = get_user_u64(val, addr + 8);
|
||||
}
|
||||
if (segv) {
|
||||
env->exception.vaddress = addr + (size == 2 ? 4 : 8);
|
||||
goto error;
|
||||
}
|
||||
if (val != env->exclusive_high) {
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
/* handle the zero register */
|
||||
val = rt == 31 ? 0 : env->xregs[rt];
|
||||
switch (size) {
|
||||
case 0:
|
||||
segv = put_user_u8(val, addr);
|
||||
break;
|
||||
case 1:
|
||||
segv = put_user_u16(val, addr);
|
||||
break;
|
||||
case 2:
|
||||
segv = put_user_u32(val, addr);
|
||||
break;
|
||||
case 3:
|
||||
segv = put_user_u64(val, addr);
|
||||
break;
|
||||
}
|
||||
if (segv) {
|
||||
goto error;
|
||||
}
|
||||
if (is_pair) {
|
||||
/* handle the zero register */
|
||||
val = rt2 == 31 ? 0 : env->xregs[rt2];
|
||||
if (size == 2) {
|
||||
segv = put_user_u32(val, addr + 4);
|
||||
} else {
|
||||
segv = put_user_u64(val, addr + 8);
|
||||
}
|
||||
if (segv) {
|
||||
env->exception.vaddress = addr + (size == 2 ? 4 : 8);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
rc = 0;
|
||||
finish:
|
||||
env->pc += 4;
|
||||
/* rs == 31 encodes a write to the ZR, thus throwing away
|
||||
* the status return. This is rather silly but valid.
|
||||
*/
|
||||
if (rs < 31) {
|
||||
env->xregs[rs] = rc;
|
||||
}
|
||||
error:
|
||||
/* instruction faulted, PC does not advance */
|
||||
/* either way a strex releases any exclusive lock we have */
|
||||
env->exclusive_addr = -1;
|
||||
end_exclusive();
|
||||
return segv;
|
||||
}
|
||||
|
||||
/* AArch64 main loop */
|
||||
void cpu_loop(CPUARMState *env)
|
||||
{
|
||||
@@ -1021,11 +816,6 @@ void cpu_loop(CPUARMState *env)
|
||||
info._sifields._sigfault._addr = env->pc;
|
||||
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
|
||||
break;
|
||||
case EXCP_STREX:
|
||||
if (!do_strex_a64(env)) {
|
||||
break;
|
||||
}
|
||||
/* fall through for segv */
|
||||
case EXCP_PREFETCH_ABORT:
|
||||
case EXCP_DATA_ABORT:
|
||||
info.si_signo = TARGET_SIGSEGV;
|
||||
@@ -1051,6 +841,9 @@ void cpu_loop(CPUARMState *env)
|
||||
case EXCP_YIELD:
|
||||
/* nothing to do here for user-mode, just resume guest code */
|
||||
break;
|
||||
case EXCP_ATOMIC:
|
||||
cpu_exec_step_atomic(cs);
|
||||
break;
|
||||
default:
|
||||
EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr);
|
||||
abort();
|
||||
@@ -1058,8 +851,6 @@ void cpu_loop(CPUARMState *env)
|
||||
process_pending_signals(env);
|
||||
/* Exception return on AArch64 always clears the exclusive monitor,
|
||||
* so any return to running guest code implies this.
|
||||
* A strex (successful or otherwise) also clears the monitor, so
|
||||
* we don't need to specialcase EXCP_STREX.
|
||||
*/
|
||||
env->exclusive_addr = -1;
|
||||
}
|
||||
@@ -1142,6 +933,9 @@ void cpu_loop(CPUUniCore32State *env)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXCP_ATOMIC:
|
||||
cpu_exec_step_atomic(cs);
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
@@ -1415,6 +1209,9 @@ void cpu_loop (CPUSPARCState *env)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXCP_ATOMIC:
|
||||
cpu_exec_step_atomic(cs);
|
||||
break;
|
||||
default:
|
||||
printf ("Unhandled trap: 0x%x\n", trapnr);
|
||||
cpu_dump_state(cs, stderr, fprintf, 0);
|
||||
@@ -1954,6 +1751,9 @@ void cpu_loop(CPUPPCState *env)
|
||||
case EXCP_INTERRUPT:
|
||||
/* just indicate that signals should be handled asap */
|
||||
break;
|
||||
case EXCP_ATOMIC:
|
||||
cpu_exec_step_atomic(cs);
|
||||
break;
|
||||
default:
|
||||
cpu_abort(cs, "Unknown exception 0x%x. Aborting\n", trapnr);
|
||||
break;
|
||||
@@ -2649,6 +2449,9 @@ done_syscall:
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXCP_ATOMIC:
|
||||
cpu_exec_step_atomic(cs);
|
||||
break;
|
||||
default:
|
||||
error:
|
||||
EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr);
|
||||
@@ -2736,6 +2539,9 @@ void cpu_loop(CPUOpenRISCState *env)
|
||||
case EXCP_NR:
|
||||
qemu_log_mask(CPU_LOG_INT, "\nNR\n");
|
||||
break;
|
||||
case EXCP_ATOMIC:
|
||||
cpu_exec_step_atomic(cs);
|
||||
break;
|
||||
default:
|
||||
EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n",
|
||||
trapnr);
|
||||
@@ -2812,6 +2618,9 @@ void cpu_loop(CPUSH4State *env)
|
||||
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
|
||||
break;
|
||||
|
||||
case EXCP_ATOMIC:
|
||||
cpu_exec_step_atomic(cs);
|
||||
break;
|
||||
default:
|
||||
printf ("Unhandled trap: 0x%x\n", trapnr);
|
||||
cpu_dump_state(cs, stderr, fprintf, 0);
|
||||
@@ -2879,6 +2688,9 @@ void cpu_loop(CPUCRISState *env)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXCP_ATOMIC:
|
||||
cpu_exec_step_atomic(cs);
|
||||
break;
|
||||
default:
|
||||
printf ("Unhandled trap: 0x%x\n", trapnr);
|
||||
cpu_dump_state(cs, stderr, fprintf, 0);
|
||||
@@ -2995,6 +2807,9 @@ void cpu_loop(CPUMBState *env)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXCP_ATOMIC:
|
||||
cpu_exec_step_atomic(cs);
|
||||
break;
|
||||
default:
|
||||
printf ("Unhandled trap: 0x%x\n", trapnr);
|
||||
cpu_dump_state(cs, stderr, fprintf, 0);
|
||||
@@ -3098,6 +2913,9 @@ void cpu_loop(CPUM68KState *env)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXCP_ATOMIC:
|
||||
cpu_exec_step_atomic(cs);
|
||||
break;
|
||||
default:
|
||||
EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr);
|
||||
abort();
|
||||
@@ -3108,51 +2926,6 @@ void cpu_loop(CPUM68KState *env)
|
||||
#endif /* TARGET_M68K */
|
||||
|
||||
#ifdef TARGET_ALPHA
|
||||
static void do_store_exclusive(CPUAlphaState *env, int reg, int quad)
|
||||
{
|
||||
target_ulong addr, val, tmp;
|
||||
target_siginfo_t info;
|
||||
int ret = 0;
|
||||
|
||||
addr = env->lock_addr;
|
||||
tmp = env->lock_st_addr;
|
||||
env->lock_addr = -1;
|
||||
env->lock_st_addr = 0;
|
||||
|
||||
start_exclusive();
|
||||
mmap_lock();
|
||||
|
||||
if (addr == tmp) {
|
||||
if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) {
|
||||
goto do_sigsegv;
|
||||
}
|
||||
|
||||
if (val == env->lock_value) {
|
||||
tmp = env->ir[reg];
|
||||
if (quad ? put_user_u64(tmp, addr) : put_user_u32(tmp, addr)) {
|
||||
goto do_sigsegv;
|
||||
}
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
env->ir[reg] = ret;
|
||||
env->pc += 4;
|
||||
|
||||
mmap_unlock();
|
||||
end_exclusive();
|
||||
return;
|
||||
|
||||
do_sigsegv:
|
||||
mmap_unlock();
|
||||
end_exclusive();
|
||||
|
||||
info.si_signo = TARGET_SIGSEGV;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TARGET_SEGV_MAPERR;
|
||||
info._sifields._sigfault._addr = addr;
|
||||
queue_signal(env, TARGET_SIGSEGV, QEMU_SI_FAULT, &info);
|
||||
}
|
||||
|
||||
void cpu_loop(CPUAlphaState *env)
|
||||
{
|
||||
CPUState *cs = CPU(alpha_env_get_cpu(env));
|
||||
@@ -3327,13 +3100,12 @@ void cpu_loop(CPUAlphaState *env)
|
||||
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
|
||||
}
|
||||
break;
|
||||
case EXCP_STL_C:
|
||||
case EXCP_STQ_C:
|
||||
do_store_exclusive(env, env->error_code, trapnr - EXCP_STL_C);
|
||||
break;
|
||||
case EXCP_INTERRUPT:
|
||||
/* Just indicate that signals should be handled asap. */
|
||||
break;
|
||||
case EXCP_ATOMIC:
|
||||
cpu_exec_step_atomic(cs);
|
||||
break;
|
||||
default:
|
||||
printf ("Unhandled trap: 0x%x\n", trapnr);
|
||||
cpu_dump_state(cs, stderr, fprintf, 0);
|
||||
@@ -3463,6 +3235,9 @@ void cpu_loop(CPUS390XState *env)
|
||||
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
|
||||
break;
|
||||
|
||||
case EXCP_ATOMIC:
|
||||
cpu_exec_step_atomic(cs);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr);
|
||||
cpu_dump_state(cs, stderr, fprintf, 0);
|
||||
@@ -3717,6 +3492,9 @@ void cpu_loop(CPUTLGState *env)
|
||||
case TILEGX_EXCP_REG_UDN_ACCESS:
|
||||
gen_sigill_reg(env);
|
||||
break;
|
||||
case EXCP_ATOMIC:
|
||||
cpu_exec_step_atomic(cs);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "trapnr is %d[0x%x].\n", trapnr, trapnr);
|
||||
g_assert_not_reached();
|
||||
|
||||
@@ -6164,6 +6164,14 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp,
|
||||
sigfillset(&sigmask);
|
||||
sigprocmask(SIG_BLOCK, &sigmask, &info.sigmask);
|
||||
|
||||
/* If this is our first additional thread, we need to ensure we
|
||||
* generate code for parallel execution and flush old translations.
|
||||
*/
|
||||
if (!parallel_cpus) {
|
||||
parallel_cpus = true;
|
||||
tb_flush(cpu);
|
||||
}
|
||||
|
||||
ret = pthread_create(&info.thread, &attr, clone_func, &info);
|
||||
/* TODO: Free new CPU state if thread creation failed. */
|
||||
|
||||
|
||||
+10
-94
@@ -21,12 +21,6 @@
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "qemu/timer.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "exec/memory.h"
|
||||
|
||||
#define DATA_SIZE (1 << SHIFT)
|
||||
|
||||
#if DATA_SIZE == 8
|
||||
#define SUFFIX q
|
||||
#define LSUFFIX q
|
||||
@@ -84,14 +78,6 @@
|
||||
# define BSWAP(X) (X)
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
# define TGT_BE(X) (X)
|
||||
# define TGT_LE(X) BSWAP(X)
|
||||
#else
|
||||
# define TGT_BE(X) BSWAP(X)
|
||||
# define TGT_LE(X) (X)
|
||||
#endif
|
||||
|
||||
#if DATA_SIZE == 1
|
||||
# define helper_le_ld_name glue(glue(helper_ret_ld, USUFFIX), MMUSUFFIX)
|
||||
# define helper_be_ld_name helper_le_ld_name
|
||||
@@ -108,35 +94,14 @@
|
||||
# define helper_be_st_name glue(glue(helper_be_st, SUFFIX), MMUSUFFIX)
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
# define helper_te_ld_name helper_be_ld_name
|
||||
# define helper_te_st_name helper_be_st_name
|
||||
#else
|
||||
# define helper_te_ld_name helper_le_ld_name
|
||||
# define helper_te_st_name helper_le_st_name
|
||||
#endif
|
||||
|
||||
#ifndef SOFTMMU_CODE_ACCESS
|
||||
static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
|
||||
CPUIOTLBEntry *iotlbentry,
|
||||
size_t mmu_idx, size_t index,
|
||||
target_ulong addr,
|
||||
uintptr_t retaddr)
|
||||
{
|
||||
uint64_t val;
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
hwaddr physaddr = iotlbentry->addr;
|
||||
MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
|
||||
|
||||
physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
|
||||
cpu->mem_io_pc = retaddr;
|
||||
if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) {
|
||||
cpu_io_recompile(cpu, retaddr);
|
||||
}
|
||||
|
||||
cpu->mem_io_vaddr = addr;
|
||||
memory_region_dispatch_read(mr, physaddr, &val, 1 << SHIFT,
|
||||
iotlbentry->attrs);
|
||||
return val;
|
||||
CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
|
||||
return io_readx(env, iotlbentry, addr, retaddr, DATA_SIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -167,15 +132,13 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||
|
||||
/* Handle an IO access. */
|
||||
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||
CPUIOTLBEntry *iotlbentry;
|
||||
if ((addr & (DATA_SIZE - 1)) != 0) {
|
||||
goto do_unaligned_access;
|
||||
}
|
||||
iotlbentry = &env->iotlb[mmu_idx][index];
|
||||
|
||||
/* ??? Note that the io helpers always read data in the target
|
||||
byte ordering. We should push the LE/BE request down into io. */
|
||||
res = glue(io_read, SUFFIX)(env, iotlbentry, addr, retaddr);
|
||||
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr);
|
||||
res = TGT_LE(res);
|
||||
return res;
|
||||
}
|
||||
@@ -236,15 +199,13 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
||||
|
||||
/* Handle an IO access. */
|
||||
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||
CPUIOTLBEntry *iotlbentry;
|
||||
if ((addr & (DATA_SIZE - 1)) != 0) {
|
||||
goto do_unaligned_access;
|
||||
}
|
||||
iotlbentry = &env->iotlb[mmu_idx][index];
|
||||
|
||||
/* ??? Note that the io helpers always read data in the target
|
||||
byte ordering. We should push the LE/BE request down into io. */
|
||||
res = glue(io_read, SUFFIX)(env, iotlbentry, addr, retaddr);
|
||||
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr);
|
||||
res = TGT_BE(res);
|
||||
return res;
|
||||
}
|
||||
@@ -295,24 +256,13 @@ WORD_TYPE helper_be_lds_name(CPUArchState *env, target_ulong addr,
|
||||
#endif
|
||||
|
||||
static inline void glue(io_write, SUFFIX)(CPUArchState *env,
|
||||
CPUIOTLBEntry *iotlbentry,
|
||||
size_t mmu_idx, size_t index,
|
||||
DATA_TYPE val,
|
||||
target_ulong addr,
|
||||
uintptr_t retaddr)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
hwaddr physaddr = iotlbentry->addr;
|
||||
MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
|
||||
|
||||
physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
|
||||
if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) {
|
||||
cpu_io_recompile(cpu, retaddr);
|
||||
}
|
||||
|
||||
cpu->mem_io_vaddr = addr;
|
||||
cpu->mem_io_pc = retaddr;
|
||||
memory_region_dispatch_write(mr, physaddr, val, 1 << SHIFT,
|
||||
iotlbentry->attrs);
|
||||
CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
|
||||
return io_writex(env, iotlbentry, val, addr, retaddr, DATA_SIZE);
|
||||
}
|
||||
|
||||
void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
@@ -340,16 +290,14 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
|
||||
/* Handle an IO access. */
|
||||
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||
CPUIOTLBEntry *iotlbentry;
|
||||
if ((addr & (DATA_SIZE - 1)) != 0) {
|
||||
goto do_unaligned_access;
|
||||
}
|
||||
iotlbentry = &env->iotlb[mmu_idx][index];
|
||||
|
||||
/* ??? Note that the io helpers always read data in the target
|
||||
byte ordering. We should push the LE/BE request down into io. */
|
||||
val = TGT_LE(val);
|
||||
glue(io_write, SUFFIX)(env, iotlbentry, val, addr, retaddr);
|
||||
glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -418,16 +366,14 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
|
||||
/* Handle an IO access. */
|
||||
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||
CPUIOTLBEntry *iotlbentry;
|
||||
if ((addr & (DATA_SIZE - 1)) != 0) {
|
||||
goto do_unaligned_access;
|
||||
}
|
||||
iotlbentry = &env->iotlb[mmu_idx][index];
|
||||
|
||||
/* ??? Note that the io helpers always read data in the target
|
||||
byte ordering. We should push the LE/BE request down into io. */
|
||||
val = TGT_BE(val);
|
||||
glue(io_write, SUFFIX)(env, iotlbentry, val, addr, retaddr);
|
||||
glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -466,33 +412,9 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val);
|
||||
}
|
||||
#endif /* DATA_SIZE > 1 */
|
||||
|
||||
#if DATA_SIZE == 1
|
||||
/* Probe for whether the specified guest write access is permitted.
|
||||
* If it is not permitted then an exception will be taken in the same
|
||||
* way as if this were a real write access (and we will not return).
|
||||
* Otherwise the function will return, and there will be a valid
|
||||
* entry in the TLB for this access.
|
||||
*/
|
||||
void probe_write(CPUArchState *env, target_ulong addr, 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;
|
||||
|
||||
if ((addr & TARGET_PAGE_MASK)
|
||||
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||
/* TLB entry is for a different page */
|
||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, MMU_DATA_STORE, mmu_idx, retaddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif /* !defined(SOFTMMU_CODE_ACCESS) */
|
||||
|
||||
#undef READ_ACCESS_TYPE
|
||||
#undef SHIFT
|
||||
#undef DATA_TYPE
|
||||
#undef SUFFIX
|
||||
#undef LSUFFIX
|
||||
@@ -503,15 +425,9 @@ void probe_write(CPUArchState *env, target_ulong addr, int mmu_idx,
|
||||
#undef USUFFIX
|
||||
#undef SSUFFIX
|
||||
#undef BSWAP
|
||||
#undef TGT_BE
|
||||
#undef TGT_LE
|
||||
#undef CPU_BE
|
||||
#undef CPU_LE
|
||||
#undef helper_le_ld_name
|
||||
#undef helper_be_ld_name
|
||||
#undef helper_le_lds_name
|
||||
#undef helper_be_lds_name
|
||||
#undef helper_le_st_name
|
||||
#undef helper_be_st_name
|
||||
#undef helper_te_ld_name
|
||||
#undef helper_te_st_name
|
||||
|
||||
+7
-15
@@ -201,7 +201,7 @@ enum {
|
||||
|
||||
/* MMU modes definitions */
|
||||
|
||||
/* Alpha has 5 MMU modes: PALcode, kernel, executive, supervisor, and user.
|
||||
/* Alpha has 5 MMU modes: PALcode, Kernel, Executive, Supervisor, and User.
|
||||
The Unix PALcode only exposes the kernel and user modes; presumably
|
||||
executive and supervisor are used by VMS.
|
||||
|
||||
@@ -209,22 +209,18 @@ enum {
|
||||
there are PALmode instructions that can access data via physical mode
|
||||
or via an os-installed "alternate mode", which is one of the 4 above.
|
||||
|
||||
QEMU does not currently properly distinguish between code/data when
|
||||
looking up addresses. To avoid having to address this issue, our
|
||||
emulated PALcode will cheat and use the KSEG mapping for its code+data
|
||||
rather than physical addresses.
|
||||
That said, we're only emulating Unix PALcode, and not attempting VMS,
|
||||
so we don't need to implement Executive and Supervisor. QEMU's own
|
||||
PALcode cheats and usees the KSEG mapping for its code+data rather than
|
||||
physical addresses. */
|
||||
|
||||
Moreover, we're only emulating Unix PALcode, and not attempting VMS.
|
||||
|
||||
All of which allows us to drop all but kernel and user modes.
|
||||
Elide the unused MMU modes to save space. */
|
||||
|
||||
#define NB_MMU_MODES 2
|
||||
#define NB_MMU_MODES 3
|
||||
|
||||
#define MMU_MODE0_SUFFIX _kernel
|
||||
#define MMU_MODE1_SUFFIX _user
|
||||
#define MMU_KERNEL_IDX 0
|
||||
#define MMU_USER_IDX 1
|
||||
#define MMU_PHYS_IDX 2
|
||||
|
||||
typedef struct CPUAlphaState CPUAlphaState;
|
||||
|
||||
@@ -234,7 +230,6 @@ struct CPUAlphaState {
|
||||
uint64_t pc;
|
||||
uint64_t unique;
|
||||
uint64_t lock_addr;
|
||||
uint64_t lock_st_addr;
|
||||
uint64_t lock_value;
|
||||
|
||||
/* The FPCR, and disassembled portions thereof. */
|
||||
@@ -350,9 +345,6 @@ enum {
|
||||
EXCP_ARITH,
|
||||
EXCP_FEN,
|
||||
EXCP_CALL_PAL,
|
||||
/* For Usermode emulation. */
|
||||
EXCP_STL_C,
|
||||
EXCP_STQ_C,
|
||||
};
|
||||
|
||||
/* Alpha-specific interrupt pending bits. */
|
||||
|
||||
@@ -126,6 +126,14 @@ static int get_physical_address(CPUAlphaState *env, target_ulong addr,
|
||||
int prot = 0;
|
||||
int ret = MM_K_ACV;
|
||||
|
||||
/* Handle physical accesses. */
|
||||
if (mmu_idx == MMU_PHYS_IDX) {
|
||||
phys = addr;
|
||||
prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Ensure that the virtual address is properly sign-extended from
|
||||
the last implemented virtual address bit. */
|
||||
if (saddr >> TARGET_VIRT_ADDR_SPACE_BITS != saddr >> 63) {
|
||||
@@ -298,12 +306,6 @@ void alpha_cpu_do_interrupt(CPUState *cs)
|
||||
case EXCP_CALL_PAL:
|
||||
name = "call_pal";
|
||||
break;
|
||||
case EXCP_STL_C:
|
||||
name = "stl_c";
|
||||
break;
|
||||
case EXCP_STQ_C:
|
||||
name = "stq_c";
|
||||
break;
|
||||
}
|
||||
qemu_log("INT %6d: %s(%#x) pc=%016" PRIx64 " sp=%016" PRIx64 "\n",
|
||||
++count, name, env->error_code, env->pc, env->ir[IR_SP]);
|
||||
|
||||
@@ -92,15 +92,6 @@ DEF_HELPER_FLAGS_2(ieee_input_cmp, TCG_CALL_NO_WG, void, env, i64)
|
||||
DEF_HELPER_FLAGS_2(ieee_input_s, TCG_CALL_NO_WG, void, env, i64)
|
||||
|
||||
#if !defined (CONFIG_USER_ONLY)
|
||||
DEF_HELPER_2(ldl_phys, i64, env, i64)
|
||||
DEF_HELPER_2(ldq_phys, i64, env, i64)
|
||||
DEF_HELPER_2(ldl_l_phys, i64, env, i64)
|
||||
DEF_HELPER_2(ldq_l_phys, i64, env, i64)
|
||||
DEF_HELPER_3(stl_phys, void, env, i64, i64)
|
||||
DEF_HELPER_3(stq_phys, void, env, i64, i64)
|
||||
DEF_HELPER_3(stl_c_phys, i64, env, i64, i64)
|
||||
DEF_HELPER_3(stq_c_phys, i64, env, i64, i64)
|
||||
|
||||
DEF_HELPER_FLAGS_1(tbia, TCG_CALL_NO_RWG, void, env)
|
||||
DEF_HELPER_FLAGS_2(tbis, TCG_CALL_NO_RWG, void, env, i64)
|
||||
DEF_HELPER_FLAGS_1(tb_flush, TCG_CALL_NO_RWG, void, env)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user