Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Open sidebar
Xing Lin
qemu
Commits
2e70f6ef
Commit
2e70f6ef
authored
Jun 29, 2008
by
pbrook
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add instruction counter.
git-svn-id:
svn://svn.savannah.nongnu.org/qemu/trunk@4799
c046a42c-6fe2-441c-8c8c-71466251a162
parent
f6e5889e
Changes
29
Hide whitespace changes
Inline
Side-by-side
Showing
29 changed files
with
830 additions
and
117 deletions
+830
-117
cpu-all.h
cpu-all.h
+2
-0
cpu-defs.h
cpu-defs.h
+28
-6
cpu-exec.c
cpu-exec.c
+63
-30
exec-all.h
exec-all.h
+27
-6
exec.c
exec.c
+118
-29
hw/mips_timer.c
hw/mips_timer.c
+5
-0
qemu-doc.texi
qemu-doc.texi
+11
-0
softmmu_template.h
softmmu_template.h
+17
-5
target-alpha/cpu.h
target-alpha/cpu.h
+2
-0
target-alpha/translate.c
target-alpha/translate.c
+32
-1
target-arm/cpu.h
target-arm/cpu.h
+2
-0
target-arm/translate.c
target-arm/translate.c
+30
-1
target-cris/cpu.h
target-cris/cpu.h
+2
-0
target-cris/translate.c
target-cris/translate.c
+20
-1
target-i386/cpu.h
target-i386/cpu.h
+2
-0
target-i386/translate.c
target-i386/translate.c
+61
-4
target-m68k/cpu.h
target-m68k/cpu.h
+2
-0
target-m68k/translate.c
target-m68k/translate.c
+20
-1
target-mips/cpu.h
target-mips/cpu.h
+6
-0
target-mips/translate.c
target-mips/translate.c
+51
-0
target-ppc/cpu.h
target-ppc/cpu.h
+3
-0
target-ppc/helper.c
target-ppc/helper.c
+1
-0
target-ppc/translate.c
target-ppc/translate.c
+31
-1
target-sh4/cpu.h
target-sh4/cpu.h
+5
-0
target-sh4/translate.c
target-sh4/translate.c
+31
-0
target-sparc/cpu.h
target-sparc/cpu.h
+5
-0
target-sparc/translate.c
target-sparc/translate.c
+19
-1
translate-all.c
translate-all.c
+9
-0
vl.c
vl.c
+225
-31
No files found.
cpu-all.h
View file @
2e70f6ef
...
...
@@ -782,6 +782,8 @@ void cpu_abort(CPUState *env, const char *fmt, ...)
__attribute__
((
__noreturn__
));
extern
CPUState
*
first_cpu
;
extern
CPUState
*
cpu_single_env
;
extern
int64_t
qemu_icount
;
extern
int
use_icount
;
#define CPU_INTERRUPT_EXIT 0x01
/* wants exit from main loop */
#define CPU_INTERRUPT_HARD 0x02
/* hardware interrupt pending */
...
...
cpu-defs.h
View file @
2e70f6ef
...
...
@@ -130,17 +130,29 @@ typedef struct CPUTLBEntry {
sizeof
(
target_phys_addr_t
))];
}
CPUTLBEntry
;
#ifdef WORDS_BIGENDIAN
typedef
struct
icount_decr_u16
{
uint16_t
high
;
uint16_t
low
;
}
icount_decr_u16
;
#else
typedef
struct
icount_decr_u16
{
uint16_t
low
;
uint16_t
high
;
}
icount_decr_u16
;
#endif
#define CPU_TEMP_BUF_NLONGS 128
#define CPU_COMMON \
struct TranslationBlock *current_tb;
/* currently executing TB */
\
/* soft mmu support */
\
/* in order to avoid passing too many arguments to the
memory
\
write
helpers, we store some rarely used information in the CPU \
/* in order to avoid passing too many arguments to the
MMIO
\
helpers, we store some rarely used information in the CPU
\
context) */
\
unsigned long mem_
write
_pc;
/* host pc at which the memory was \
written
*/
\
target_ulong mem_
write
_vaddr;
/* target virtual addr at which the \
memory was
written
*/
\
unsigned long mem_
io
_pc;
/* host pc at which the memory was
\
accessed
*/
\
target_ulong mem_
io
_vaddr;
/* target virtual addr at which the
\
memory was
accessed
*/
\
int halted;
/* TRUE if the CPU is in suspend state */
\
/* The meaning of the MMU modes is defined in the target code. */
\
CPUTLBEntry tlb_table[NB_MMU_MODES][CPU_TLB_SIZE]; \
...
...
@@ -149,6 +161,16 @@ typedef struct CPUTLBEntry {
/* buffer for temporaries in the code generator */
\
long temp_buf[CPU_TEMP_BUF_NLONGS]; \
\
int64_t icount_extra;
/* Instructions until next timer event. */
\
/* Number of cycles left, with interrupt flag in high bit. \
This allows a single read-compare-cbranch-write sequence to test \
for both decrementer underflow and exceptions. */
\
union { \
uint32_t u32; \
icount_decr_u16 u16; \
} icount_decr; \
uint32_t can_do_io;
/* nonzero if memory mapped IO is safe. */
\
\
/* from this point: preserved by CPU reset */
\
/* ice debug support */
\
target_ulong breakpoints[MAX_BREAKPOINTS]; \
...
...
cpu-exec.c
View file @
2e70f6ef
...
...
@@ -82,15 +82,40 @@ void cpu_resume_from_signal(CPUState *env1, void *puc)
longjmp
(
env
->
jmp_env
,
1
);
}
/* Execute the code without caching the generated code. An interpreter
could be used if available. */
static
void
cpu_exec_nocache
(
int
max_cycles
,
TranslationBlock
*
orig_tb
)
{
unsigned
long
next_tb
;
TranslationBlock
*
tb
;
/* Should never happen.
We only end up here when an existing TB is too long. */
if
(
max_cycles
>
CF_COUNT_MASK
)
max_cycles
=
CF_COUNT_MASK
;
tb
=
tb_gen_code
(
env
,
orig_tb
->
pc
,
orig_tb
->
cs_base
,
orig_tb
->
flags
,
max_cycles
);
env
->
current_tb
=
tb
;
/* execute the generated code */
next_tb
=
tcg_qemu_tb_exec
(
tb
->
tc_ptr
);
if
((
next_tb
&
3
)
==
2
)
{
/* Restore PC. This may happen if async event occurs before
the TB starts executing. */
CPU_PC_FROM_TB
(
env
,
tb
);
}
tb_phys_invalidate
(
tb
,
-
1
);
tb_free
(
tb
);
}
static
TranslationBlock
*
tb_find_slow
(
target_ulong
pc
,
target_ulong
cs_base
,
uint64_t
flags
)
{
TranslationBlock
*
tb
,
**
ptb1
;
int
code_gen_size
;
unsigned
int
h
;
target_ulong
phys_pc
,
phys_page1
,
phys_page2
,
virt_page2
;
uint8_t
*
tc_ptr
;
tb_invalidated_flag
=
0
;
...
...
@@ -124,30 +149,8 @@ static TranslationBlock *tb_find_slow(target_ulong pc,
ptb1
=
&
tb
->
phys_hash_next
;
}
not_found:
/* if no translated code available, then translate it now */
tb
=
tb_alloc
(
pc
);
if
(
!
tb
)
{
/* flush must be done */
tb_flush
(
env
);
/* cannot fail at this point */
tb
=
tb_alloc
(
pc
);
/* don't forget to invalidate previous TB info */
tb_invalidated_flag
=
1
;
}
tc_ptr
=
code_gen_ptr
;
tb
->
tc_ptr
=
tc_ptr
;
tb
->
cs_base
=
cs_base
;
tb
->
flags
=
flags
;
cpu_gen_code
(
env
,
tb
,
&
code_gen_size
);
code_gen_ptr
=
(
void
*
)(((
unsigned
long
)
code_gen_ptr
+
code_gen_size
+
CODE_GEN_ALIGN
-
1
)
&
~
(
CODE_GEN_ALIGN
-
1
));
/* check next page if needed */
virt_page2
=
(
pc
+
tb
->
size
-
1
)
&
TARGET_PAGE_MASK
;
phys_page2
=
-
1
;
if
((
pc
&
TARGET_PAGE_MASK
)
!=
virt_page2
)
{
phys_page2
=
get_phys_addr_code
(
env
,
virt_page2
);
}
tb_link_phys
(
tb
,
phys_pc
,
phys_page2
);
/* if no translated code available, then translate it now */
tb
=
tb_gen_code
(
env
,
pc
,
cs_base
,
flags
,
0
);
found:
/* we add the TB in the virtual pc hash table */
...
...
@@ -583,6 +586,7 @@ int cpu_exec(CPUState *env1)
of memory exceptions while generating the code, we
must recompute the hash index here */
next_tb
=
0
;
tb_invalidated_flag
=
0
;
}
#ifdef DEBUG_EXEC
if
((
loglevel
&
CPU_LOG_EXEC
))
{
...
...
@@ -604,16 +608,45 @@ int cpu_exec(CPUState *env1)
}
}
spin_unlock
(
&
tb_lock
);
tc_ptr
=
tb
->
tc_ptr
;
env
->
current_tb
=
tb
;
while
(
env
->
current_tb
)
{
tc_ptr
=
tb
->
tc_ptr
;
/* execute the generated code */
#if defined(__sparc__) && !defined(HOST_SOLARIS)
#undef env
env
=
cpu_single_env
;
env
=
cpu_single_env
;
#define env cpu_single_env
#endif
next_tb
=
tcg_qemu_tb_exec
(
tc_ptr
);
env
->
current_tb
=
NULL
;
next_tb
=
tcg_qemu_tb_exec
(
tc_ptr
);
env
->
current_tb
=
NULL
;
if
((
next_tb
&
3
)
==
2
)
{
/* Instruction counter exired. */
int
insns_left
;
tb
=
(
TranslationBlock
*
)(
long
)(
next_tb
&
~
3
);
/* Restore PC. */
CPU_PC_FROM_TB
(
env
,
tb
);
insns_left
=
env
->
icount_decr
.
u32
;
if
(
env
->
icount_extra
&&
insns_left
>=
0
)
{
/* Refill decrementer and continue execution. */
env
->
icount_extra
+=
insns_left
;
if
(
env
->
icount_extra
>
0xffff
)
{
insns_left
=
0xffff
;
}
else
{
insns_left
=
env
->
icount_extra
;
}
env
->
icount_extra
-=
insns_left
;
env
->
icount_decr
.
u16
.
low
=
insns_left
;
}
else
{
if
(
insns_left
>
0
)
{
/* Execute remaining instructions. */
cpu_exec_nocache
(
insns_left
,
tb
);
}
env
->
exception_index
=
EXCP_INTERRUPT
;
next_tb
=
0
;
cpu_loop_exit
();
}
}
}
/* reset soft MMU for next block (it can currently
only be set by a memory fault) */
#if defined(USE_KQEMU)
...
...
exec-all.h
View file @
2e70f6ef
...
...
@@ -27,7 +27,7 @@
#define DISAS_UPDATE 2
/* cpu state was modified dynamically */
#define DISAS_TB_JUMP 3
/* only pc was modified statically */
struct
TranslationBlock
;
typedef
struct
TranslationBlock
TranslationBlock
;
/* XXX: make safe guess about sizes */
#define MAX_OP_PER_INSTR 64
...
...
@@ -48,6 +48,7 @@ extern target_ulong gen_opc_pc[OPC_BUF_SIZE];
extern
target_ulong
gen_opc_npc
[
OPC_BUF_SIZE
];
extern
uint8_t
gen_opc_cc_op
[
OPC_BUF_SIZE
];
extern
uint8_t
gen_opc_instr_start
[
OPC_BUF_SIZE
];
extern
uint16_t
gen_opc_icount
[
OPC_BUF_SIZE
];
extern
target_ulong
gen_opc_jump_pc
[
2
];
extern
uint32_t
gen_opc_hflags
[
OPC_BUF_SIZE
];
...
...
@@ -75,6 +76,10 @@ int cpu_restore_state_copy(struct TranslationBlock *tb,
CPUState
*
env
,
unsigned
long
searched_pc
,
void
*
puc
);
void
cpu_resume_from_signal
(
CPUState
*
env1
,
void
*
puc
);
void
cpu_io_recompile
(
CPUState
*
env
,
void
*
retaddr
);
TranslationBlock
*
tb_gen_code
(
CPUState
*
env
,
target_ulong
pc
,
target_ulong
cs_base
,
int
flags
,
int
cflags
);
void
cpu_exec_init
(
CPUState
*
env
);
int
page_unprotect
(
target_ulong
address
,
unsigned
long
pc
,
void
*
puc
);
void
tb_invalidate_phys_page_range
(
target_phys_addr_t
start
,
target_phys_addr_t
end
,
...
...
@@ -117,16 +122,15 @@ static inline int tlb_set_page(CPUState *env1, target_ulong vaddr,
#define USE_DIRECT_JUMP
#endif
typedef
struct
TranslationBlock
{
struct
TranslationBlock
{
target_ulong
pc
;
/* simulated PC corresponding to this block (EIP + CS base) */
target_ulong
cs_base
;
/* CS base for this block */
uint64_t
flags
;
/* flags defining in which context the code was generated */
uint16_t
size
;
/* size of target code for this block (1 <=
size <= TARGET_PAGE_SIZE) */
uint16_t
cflags
;
/* compile flags */
#define CF_TB_FP_USED 0x0002
/* fp ops are used in the TB */
#define CF_FP_USED 0x0004
/* fp ops are used in the TB or in a chained TB */
#define CF_SINGLE_INSN 0x0008
/* compile only a single instruction */
#define CF_COUNT_MASK 0x7fff
#define CF_LAST_IO 0x8000
/* Last insn may be an IO access. */
uint8_t
*
tc_ptr
;
/* pointer to the translated code */
/* next matching tb for physical address. */
...
...
@@ -150,7 +154,8 @@ typedef struct TranslationBlock {
jmp_first */
struct
TranslationBlock
*
jmp_next
[
2
];
struct
TranslationBlock
*
jmp_first
;
}
TranslationBlock
;
uint32_t
icount
;
};
static
inline
unsigned
int
tb_jmp_cache_hash_page
(
target_ulong
pc
)
{
...
...
@@ -173,9 +178,11 @@ static inline unsigned int tb_phys_hash_func(unsigned long pc)
}
TranslationBlock
*
tb_alloc
(
target_ulong
pc
);
void
tb_free
(
TranslationBlock
*
tb
);
void
tb_flush
(
CPUState
*
env
);
void
tb_link_phys
(
TranslationBlock
*
tb
,
target_ulong
phys_pc
,
target_ulong
phys_page2
);
void
tb_phys_invalidate
(
TranslationBlock
*
tb
,
target_ulong
page_addr
);
extern
TranslationBlock
*
tb_phys_hash
[
CODE_GEN_PHYS_HASH_SIZE
];
extern
uint8_t
*
code_gen_ptr
;
...
...
@@ -364,6 +371,20 @@ static inline target_ulong get_phys_addr_code(CPUState *env1, target_ulong addr)
}
return
addr
+
env1
->
tlb_table
[
mmu_idx
][
page_index
].
addend
-
(
unsigned
long
)
phys_ram_base
;
}
/* Deterministic execution requires that IO only be performaed on the last
instruction of a TB so that interrupts take effect immediately. */
static
inline
int
can_do_io
(
CPUState
*
env
)
{
if
(
!
use_icount
)
return
1
;
/* If not executing code then assume we are ok. */
if
(
!
env
->
current_tb
)
return
1
;
return
env
->
can_do_io
!=
0
;
}
#endif
#ifdef USE_KQEMU
...
...
exec.c
View file @
2e70f6ef
...
...
@@ -107,6 +107,13 @@ CPUState *first_cpu;
/* current CPU in the current thread. It is only valid inside
cpu_exec() */
CPUState
*
cpu_single_env
;
/* 0 = Do not count executed instructions.
1 = Precice instruction counting.
2 = Adaptive rate instruction counting. */
int
use_icount
=
0
;
/* Current instruction counter. While executing translated code this may
include some instructions that have not yet been executed. */
int64_t
qemu_icount
;
typedef
struct
PageDesc
{
/* list of TBs intersecting this ram page */
...
...
@@ -633,7 +640,7 @@ static inline void tb_reset_jump(TranslationBlock *tb, int n)
tb_set_jmp_target
(
tb
,
n
,
(
unsigned
long
)(
tb
->
tc_ptr
+
tb
->
tb_next_offset
[
n
]));
}
static
inline
void
tb_phys_invalidate
(
TranslationBlock
*
tb
,
target_ulong
page_addr
)
void
tb_phys_invalidate
(
TranslationBlock
*
tb
,
target_ulong
page_addr
)
{
CPUState
*
env
;
PageDesc
*
p
;
...
...
@@ -746,11 +753,9 @@ static void build_page_bitmap(PageDesc *p)
}
}
#ifdef TARGET_HAS_PRECISE_SMC
static
void
tb_gen_code
(
CPUState
*
env
,
target_ulong
pc
,
target_ulong
cs_base
,
int
flags
,
int
cflags
)
TranslationBlock
*
tb_gen_code
(
CPUState
*
env
,
target_ulong
pc
,
target_ulong
cs_base
,
int
flags
,
int
cflags
)
{
TranslationBlock
*
tb
;
uint8_t
*
tc_ptr
;
...
...
@@ -764,6 +769,8 @@ static void tb_gen_code(CPUState *env,
tb_flush
(
env
);
/* cannot fail at this point */
tb
=
tb_alloc
(
pc
);
/* Don't forget to invalidate previous TB info. */
tb_invalidated_flag
=
1
;
}
tc_ptr
=
code_gen_ptr
;
tb
->
tc_ptr
=
tc_ptr
;
...
...
@@ -780,8 +787,8 @@ static void tb_gen_code(CPUState *env,
phys_page2
=
get_phys_addr_code
(
env
,
virt_page2
);
}
tb_link_phys
(
tb
,
phys_pc
,
phys_page2
);
return
tb
;
}
#endif
/* invalidate all TBs which intersect with the target physical page
starting in range [start;end[. NOTE: start and end must refer to
...
...
@@ -836,13 +843,13 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t
if
(
current_tb_not_found
)
{
current_tb_not_found
=
0
;
current_tb
=
NULL
;
if
(
env
->
mem_
write
_pc
)
{
if
(
env
->
mem_
io
_pc
)
{
/* now we have a real cpu fault */
current_tb
=
tb_find_pc
(
env
->
mem_
write
_pc
);
current_tb
=
tb_find_pc
(
env
->
mem_
io
_pc
);
}
}
if
(
current_tb
==
tb
&&
!
(
current_tb
->
cflags
&
CF_
SINGLE_INSN
)
)
{
(
current_tb
->
cflags
&
CF_
COUNT_MASK
)
!=
1
)
{
/* If we are modifying the current TB, we must stop
its execution. We could be more precise by checking
that the modification is after the current PC, but it
...
...
@@ -851,7 +858,7 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t
current_tb_modified
=
1
;
cpu_restore_state
(
current_tb
,
env
,
env
->
mem_
write
_pc
,
NULL
);
env
->
mem_
io
_pc
,
NULL
);
#if defined(TARGET_I386)
current_flags
=
env
->
hflags
;
current_flags
|=
(
env
->
eflags
&
(
IOPL_MASK
|
TF_MASK
|
VM_MASK
));
...
...
@@ -883,7 +890,7 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t
if
(
!
p
->
first_tb
)
{
invalidate_page_bitmap
(
p
);
if
(
is_cpu_write_access
)
{
tlb_unprotect_code_phys
(
env
,
start
,
env
->
mem_
write
_vaddr
);
tlb_unprotect_code_phys
(
env
,
start
,
env
->
mem_
io
_vaddr
);
}
}
#endif
...
...
@@ -893,8 +900,7 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t
modifying the memory. It will ensure that it cannot modify
itself */
env
->
current_tb
=
NULL
;
tb_gen_code
(
env
,
current_pc
,
current_cs_base
,
current_flags
,
CF_SINGLE_INSN
);
tb_gen_code
(
env
,
current_pc
,
current_cs_base
,
current_flags
,
1
);
cpu_resume_from_signal
(
env
,
NULL
);
}
#endif
...
...
@@ -909,7 +915,7 @@ static inline void tb_invalidate_phys_page_fast(target_phys_addr_t start, int le
if (1) {
if (loglevel) {
fprintf(logfile, "modifying code at 0x%x size=%d EIP=%x PC=%08x\n",
cpu_single_env->mem_
write
_vaddr, len,
cpu_single_env->mem_
io
_vaddr, len,
cpu_single_env->eip,
cpu_single_env->eip + (long)cpu_single_env->segs[R_CS].base);
}
...
...
@@ -961,7 +967,7 @@ static void tb_invalidate_phys_page(target_phys_addr_t addr,
tb
=
(
TranslationBlock
*
)((
long
)
tb
&
~
3
);
#ifdef TARGET_HAS_PRECISE_SMC
if
(
current_tb
==
tb
&&
!
(
current_tb
->
cflags
&
CF_
SINGLE_INSN
)
)
{
(
current_tb
->
cflags
&
CF_
COUNT_MASK
)
!=
1
)
{
/* If we are modifying the current TB, we must stop
its execution. We could be more precise by checking
that the modification is after the current PC, but it
...
...
@@ -990,8 +996,7 @@ static void tb_invalidate_phys_page(target_phys_addr_t addr,
modifying the memory. It will ensure that it cannot modify
itself */
env
->
current_tb
=
NULL
;
tb_gen_code
(
env
,
current_pc
,
current_cs_base
,
current_flags
,
CF_SINGLE_INSN
);
tb_gen_code
(
env
,
current_pc
,
current_cs_base
,
current_flags
,
1
);
cpu_resume_from_signal
(
env
,
puc
);
}
#endif
...
...
@@ -1068,6 +1073,17 @@ TranslationBlock *tb_alloc(target_ulong pc)
return
tb
;
}
void
tb_free
(
TranslationBlock
*
tb
)
{
/* In practice this is mostly used for single use temorary TB
Ignore the hard cases and just back up if this TB happens to
be the last one generated. */
if
(
nb_tbs
>
0
&&
tb
==
&
tbs
[
nb_tbs
-
1
])
{
code_gen_ptr
=
tb
->
tc_ptr
;
nb_tbs
--
;
}
}
/* add a new TB and link it to the physical page tables. phys_page2 is
(-1) to indicate that only one page contains the TB. */
void
tb_link_phys
(
TranslationBlock
*
tb
,
...
...
@@ -1369,7 +1385,9 @@ void cpu_interrupt(CPUState *env, int mask)
TranslationBlock
*
tb
;
static
spinlock_t
interrupt_lock
=
SPIN_LOCK_UNLOCKED
;
#endif
int
old_mask
;
old_mask
=
env
->
interrupt_request
;
/* FIXME: This is probably not threadsafe. A different thread could
be in the mittle of a read-modify-write operation. */
env
->
interrupt_request
|=
mask
;
...
...
@@ -1379,13 +1397,25 @@ void cpu_interrupt(CPUState *env, int mask)
emulation this often isn't actually as bad as it sounds. Often
signals are used primarily to interrupt blocking syscalls. */
#else
/* if the cpu is currently executing code, we must unlink it and
all the potentially executing TB */
tb
=
env
->
current_tb
;
if
(
tb
&&
!
testandset
(
&
interrupt_lock
))
{
env
->
current_tb
=
NULL
;
tb_reset_jump_recursive
(
tb
);
resetlock
(
&
interrupt_lock
);
if
(
use_icount
)
{
env
->
icount_decr
.
u16
.
high
=
0x8000
;
#ifndef CONFIG_USER_ONLY
/* CPU_INTERRUPT_EXIT isn't a real interrupt. It just means
an async event happened and we need to process it. */
if
(
!
can_do_io
(
env
)
&&
(
mask
&
~
(
old_mask
|
CPU_INTERRUPT_EXIT
))
!=
0
)
{
cpu_abort
(
env
,
"Raised interrupt while not in I/O function"
);
}
#endif
}
else
{
tb
=
env
->
current_tb
;
/* if the cpu is currently executing code, we must unlink it and
all the potentially executing TB */
if
(
tb
&&
!
testandset
(
&
interrupt_lock
))
{
env
->
current_tb
=
NULL
;
tb_reset_jump_recursive
(
tb
);
resetlock
(
&
interrupt_lock
);
}
}
#endif
}
...
...
@@ -2227,7 +2257,7 @@ static void notdirty_mem_writeb(void *opaque, target_phys_addr_t ram_addr,
/* we remove the notdirty callback only if the code has been
flushed */
if
(
dirty_flags
==
0xff
)
tlb_set_dirty
(
cpu_single_env
,
cpu_single_env
->
mem_
write
_vaddr
);
tlb_set_dirty
(
cpu_single_env
,
cpu_single_env
->
mem_
io
_vaddr
);
}
static
void
notdirty_mem_writew
(
void
*
opaque
,
target_phys_addr_t
ram_addr
,
...
...
@@ -2252,7 +2282,7 @@ static void notdirty_mem_writew(void *opaque, target_phys_addr_t ram_addr,
/* we remove the notdirty callback only if the code has been
flushed */
if
(
dirty_flags
==
0xff
)
tlb_set_dirty
(
cpu_single_env
,
cpu_single_env
->
mem_
write
_vaddr
);
tlb_set_dirty
(
cpu_single_env
,
cpu_single_env
->
mem_
io
_vaddr
);
}
static
void
notdirty_mem_writel
(
void
*
opaque
,
target_phys_addr_t
ram_addr
,
...
...
@@ -2277,7 +2307,7 @@ static void notdirty_mem_writel(void *opaque, target_phys_addr_t ram_addr,
/* we remove the notdirty callback only if the code has been
flushed */
if
(
dirty_flags
==
0xff
)
tlb_set_dirty
(
cpu_single_env
,
cpu_single_env
->
mem_
write
_vaddr
);
tlb_set_dirty
(
cpu_single_env
,
cpu_single_env
->
mem_
io
_vaddr
);
}
static
CPUReadMemoryFunc
*
error_mem_read
[
3
]
=
{
...
...
@@ -2299,7 +2329,7 @@ static void check_watchpoint(int offset, int flags)
target_ulong
vaddr
;
int
i
;
vaddr
=
(
env
->
mem_
write
_vaddr
&
TARGET_PAGE_MASK
)
+
offset
;
vaddr
=
(
env
->
mem_
io
_vaddr
&
TARGET_PAGE_MASK
)
+
offset
;
for
(
i
=
0
;
i
<
env
->
nb_watchpoints
;
i
++
)
{
if
(
vaddr
==
env
->
watchpoint
[
i
].
vaddr
&&
(
env
->
watchpoint
[
i
].
type
&
flags
))
{
...
...
@@ -2967,6 +2997,65 @@ int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
return
0
;
}
/* in deterministic execution mode, instructions doing device I/Os
must be at the end of the TB */
void
cpu_io_recompile
(
CPUState
*
env
,
void
*
retaddr
)
{
TranslationBlock
*
tb
;
uint32_t
n
,
cflags
;
target_ulong
pc
,
cs_base
;
uint64_t
flags
;
tb
=
tb_find_pc
((
unsigned
long
)
retaddr
);
if
(
!
tb
)
{
cpu_abort
(
env
,
"cpu_io_recompile: could not find TB for pc=%p"
,
retaddr
);
}
n
=
env
->
icount_decr
.
u16
.
low
+
tb
->
icount
;
cpu_restore_state
(
tb
,
env
,
(
unsigned
long
)
retaddr
,
NULL
);
/* Calculate how many instructions had been executed before the fault
occured. */
n
=
n
-
env
->
icount_decr
.
u16
.
low
;
/* Generate a new TB ending on the I/O insn. */
n
++
;
/* On MIPS and SH, delay slot instructions can only be restarted if
they were already the first instruction in the TB. If this is not
the first instruction in a TB then re-execute the preceeding
branch. */
#if defined(TARGET_MIPS)
if
((
env
->
hflags
&
MIPS_HFLAG_BMASK
)
!=
0
&&
n
>
1
)
{
env
->
active_tc
.
PC
-=
4
;
env
->
icount_decr
.
u16
.
low
++
;
env
->
hflags
&=
~
MIPS_HFLAG_BMASK
;
}
#elif defined(TARGET_SH4)
if
((
env
->
flags
&
((
DELAY_SLOT
|
DELAY_SLOT_CONDITIONAL
)))
!=
0
&&
n
>
1
)
{
env
->
pc
-=
2
;
env
->
icount_decr
.
u16
.
low
++
;
env
->
flags
&=
~
(
DELAY_SLOT
|
DELAY_SLOT_CONDITIONAL
);
}
#endif
/* This should never happen. */
if
(
n
>
CF_COUNT_MASK
)
cpu_abort
(
env
,
"TB too big during recompile"
);
cflags
=
n
|
CF_LAST_IO
;
pc
=
tb
->
pc
;
cs_base
=
tb
->
cs_base
;
flags
=
tb
->
flags
;
tb_phys_invalidate
(
tb
,
-
1
);
/* FIXME: In theory this could raise an exception. In practice
we have already translated the block once so it's probably ok. */
tb_gen_code
(
env
,
pc
,
cs_base
,
flags
,
cflags
);
/* TODO: If env->pc != tb->pc (i.e. the failuting instruction was not
the first in the TB) then we end up generating a whole new TB and
repeating the fault, which is horribly inefficient.
Better would be to execute just this insn uncached, or generate a
second new TB. */
cpu_resume_from_signal
(
env
,
NULL
);
}
void
dump_exec_info
(
FILE
*
f
,
int
(
*
cpu_fprintf
)(
FILE
*
f
,
const
char
*
fmt
,
...))
{
...
...
hw/mips_timer.c
View file @
2e70f6ef
...
...
@@ -91,7 +91,12 @@ static void mips_timer_cb (void *opaque)
if
(
env
->
CP0_Cause
&
(
1
<<
CP0Ca_DC
))
return
;
/* ??? This callback should occur when the counter is exactly equal to
the comparator value. Offset the count by one to avoid immediately
retriggering the callback before any virtual time has passed. */
env
->
CP0_Count
++
;
cpu_mips_timer_update
(
env
);
env
->
CP0_Count
--
;
if
(
env
->
insn_flags
&
ISA_MIPS32R2
)
env
->
CP0_Cause
|=
1
<<
CP0Ca_TI
;
qemu_irq_raise
(
env
->
irq
[(
env
->
CP0_IntCtl
>>
CP0IntCtl_IPTI
)
&
0x7
]);
...
...
qemu-doc.texi
View file @
2e70f6ef
...
...
@@ -965,6 +965,17 @@ On M68K this implements the "ColdFire GDB" interface used by libgloss.
Note that this allows guest direct access to the host filesystem,
so should only be used with trusted guest OS.
@item -icount [N|auto]
Enable virtual instruction counter. The virtual cpu will execute one
instruction every 2
^
N ns of virtual time. If @code
{
auto
}
is specified
then the virtual cpu speed will be automatically adjusted to keep virtual
time within a few seconds of real time.
Note that while this option can give deterministic behavior, it does not
provide cycle accurate emulation. Modern CPUs contain superscalar out of
order cores with complex cache heirachies. The number of instructions
executed often has little or no correlation with actual performance.
@end table
@c man end
...
...
softmmu_template.h
View file @
2e70f6ef
...
...
@@ -51,12 +51,18 @@ static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
int
mmu_idx
,
void
*
retaddr
);
static
inline
DATA_TYPE
glue
(
io_read
,
SUFFIX
)(
target_phys_addr_t
physaddr
,
target_ulong
addr
)
target_ulong
addr
,
void
*
retaddr
)
{
DATA_TYPE
res
;
int
index
;
index
=
(
physaddr
>>
IO_MEM_SHIFT
)
&
(
IO_MEM_NB_ENTRIES
-
1
);
physaddr
=
(
physaddr
&
TARGET_PAGE_MASK
)
+
addr
;
env
->
mem_io_pc
=
(
unsigned
long
)
retaddr
;
if
(
index
>
(
IO_MEM_NOTDIRTY
>>
IO_MEM_SHIFT
)
&&
!
can_do_io
(
env
))
{
cpu_io_recompile
(
env
,
retaddr
);
}
#if SHIFT <= 2
res
=
io_mem_read
[
index
][
SHIFT
](
io_mem_opaque
[
index
],
physaddr
);
...
...
@@ -95,8 +101,9 @@ DATA_TYPE REGPARM glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
/* IO access */
if
((
addr
&
(
DATA_SIZE
-
1
))
!=
0
)
goto
do_unaligned_access
;
retaddr
=
GETPC
();
addend
=
env
->
iotlb
[
mmu_idx
][
index
];
res
=
glue
(
io_read
,
SUFFIX
)(
addend
,
addr
);
res
=
glue
(
io_read
,
SUFFIX
)(
addend
,
addr
,
retaddr
);
}
else
if
(((
addr
&
~
TARGET_PAGE_MASK
)
+
DATA_SIZE
-
1
)
>=
TARGET_PAGE_SIZE
)
{