b7b1a4d6b4
quit_request is for internal use. This means the IP register was updated and qemu needs to rebuild the translation blocks. stop_request is set by the user (uc_emu_stop) to indecate that unicorn sould stop emulating.
236 lines
6.3 KiB
C
236 lines
6.3 KiB
C
/*
|
|
* QEMU System Emulator
|
|
*
|
|
* Copyright (c) 2003-2008 Fabrice Bellard
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
#include "sysemu/tcg.h"
|
|
#include "sysemu/cpus.h"
|
|
#include "qemu/bitmap.h"
|
|
#include "tcg/tcg.h"
|
|
#include "exec/tb-hash.h"
|
|
#include "accel/tcg/translate-all.h"
|
|
|
|
#include "uc_priv.h"
|
|
|
|
|
|
int64_t cpu_icount_to_ns(int64_t icount)
|
|
{
|
|
// return icount << atomic_read(&timers_state.icount_time_shift);
|
|
// from configure_icount(QemuOpts *opts, Error **errp)
|
|
/* 125MIPS seems a reasonable initial guess at the guest speed.
|
|
It will be corrected fairly quickly anyway. */
|
|
// timers_state.icount_time_shift = 3;
|
|
|
|
return icount << 3;
|
|
}
|
|
|
|
bool cpu_is_stopped(CPUState *cpu)
|
|
{
|
|
return cpu->stopped;
|
|
}
|
|
|
|
/* return the time elapsed in VM between vm_start and vm_stop. Unless
|
|
* icount is active, cpu_get_ticks() uses units of the host CPU cycle
|
|
* counter.
|
|
*/
|
|
int64_t cpu_get_ticks(void)
|
|
{
|
|
return cpu_get_host_ticks();
|
|
}
|
|
|
|
/* Return the monotonic time elapsed in VM, i.e.,
|
|
* the time between vm_start and vm_stop
|
|
*/
|
|
int64_t cpu_get_clock(void)
|
|
{
|
|
return get_clock();
|
|
}
|
|
|
|
static bool cpu_can_run(CPUState *cpu)
|
|
{
|
|
if (cpu->stop) {
|
|
return false;
|
|
}
|
|
if (cpu_is_stopped(cpu)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void cpu_handle_guest_debug(CPUState *cpu)
|
|
{
|
|
cpu->stopped = true;
|
|
}
|
|
|
|
static int tcg_cpu_exec(struct uc_struct *uc)
|
|
{
|
|
int r;
|
|
bool finish = false;
|
|
|
|
while (!uc->exit_request) {
|
|
CPUState *cpu = uc->cpu;
|
|
|
|
//qemu_clock_enable(QEMU_CLOCK_VIRTUAL,
|
|
// (cpu->singlestep_enabled & SSTEP_NOTIMER) == 0);
|
|
if (cpu_can_run(cpu)) {
|
|
uc->quit_request = false;
|
|
r = cpu_exec(uc, cpu);
|
|
|
|
// quit current TB but continue emulating?
|
|
if (uc->quit_request && !uc->stop_request) {
|
|
// reset stop_request
|
|
uc->stop_request = false;
|
|
|
|
// resume cpu
|
|
cpu->halted = 0;
|
|
cpu->exit_request = 0;
|
|
cpu->exception_index = -1;
|
|
cpu_resume(cpu);
|
|
} else if (uc->stop_request) {
|
|
//printf(">>> got STOP request!!!\n");
|
|
finish = true;
|
|
break;
|
|
}
|
|
|
|
// save invalid memory access error & quit
|
|
if (uc->invalid_error) {
|
|
// printf(">>> invalid memory accessed, STOP = %u!!!\n", env->invalid_error);
|
|
finish = true;
|
|
break;
|
|
}
|
|
|
|
// printf(">>> stop with r = %x, HLT=%x\n", r, EXCP_HLT);
|
|
if (r == EXCP_DEBUG) {
|
|
cpu_handle_guest_debug(cpu);
|
|
break;
|
|
}
|
|
if (r == EXCP_HLT) {
|
|
//printf(">>> got HLT!!!\n");
|
|
finish = true;
|
|
break;
|
|
}
|
|
} else if (cpu->stop || cpu->stopped) {
|
|
// printf(">>> got stopped!!!\n");
|
|
break;
|
|
}
|
|
}
|
|
uc->exit_request = 0;
|
|
uc->cpu->exit_request = 0;
|
|
uc->cpu->icount_decr_ptr->u16.high = 0;
|
|
uc->cpu->tcg_exit_req = 0;
|
|
|
|
return finish;
|
|
}
|
|
|
|
void cpu_resume(CPUState *cpu)
|
|
{
|
|
cpu->stop = false;
|
|
cpu->stopped = false;
|
|
}
|
|
|
|
static void qemu_tcg_init_vcpu(CPUState *cpu)
|
|
{
|
|
/*
|
|
* Initialize TCG regions--once. Now is a good time, because:
|
|
* (1) TCG's init context, prologue and target globals have been set up.
|
|
* (2) qemu_tcg_mttcg_enabled() works now (TCG init code runs before the
|
|
* -accel flag is processed, so the check doesn't work then).
|
|
*/
|
|
tcg_region_init(cpu->uc->tcg_ctx);
|
|
|
|
cpu->created = true;
|
|
}
|
|
|
|
void qemu_init_vcpu(CPUState *cpu)
|
|
{
|
|
cpu->nr_cores = 1;
|
|
cpu->nr_threads = 1;
|
|
cpu->stopped = true;
|
|
|
|
qemu_tcg_init_vcpu(cpu);
|
|
|
|
return;
|
|
}
|
|
|
|
void cpu_stop_current(struct uc_struct *uc)
|
|
{
|
|
if (uc->cpu) {
|
|
uc->cpu->stop = false;
|
|
uc->cpu->stopped = true;
|
|
cpu_exit(uc->cpu);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static inline gboolean uc_exit_invalidate_iter(gpointer key, gpointer val, gpointer data)
|
|
{
|
|
uint64_t exit = *((uint64_t*)key);
|
|
uc_engine *uc = (uc_engine*)data;
|
|
|
|
if (exit != 0) {
|
|
// Unicorn: Why addr - 1?
|
|
//
|
|
// 0: INC ecx
|
|
// 1: DEC edx <--- We put exit here, then the range of TB is [0, 1)
|
|
//
|
|
// While tb_invalidate_phys_range invalides [start, end)
|
|
//
|
|
// This function is designed to used with g_tree_foreach
|
|
uc->uc_invalidate_tb(uc, exit - 1, 1);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void resume_all_vcpus(struct uc_struct* uc)
|
|
{
|
|
CPUState *cpu = uc->cpu;
|
|
cpu->halted = 0;
|
|
cpu->exit_request = 0;
|
|
cpu->exception_index = -1;
|
|
cpu_resume(cpu);
|
|
/* static void qemu_tcg_cpu_loop(struct uc_struct *uc) */
|
|
cpu->created = true;
|
|
while (true) {
|
|
if (tcg_cpu_exec(uc)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// clear the cache of the exits address, since the generated code
|
|
// at that address is to exit emulation, but not for the instruction there.
|
|
// if we dont do this, next time we cannot emulate at that address
|
|
if (uc->use_exits) {
|
|
g_tree_foreach(uc->ctl_exits, uc_exit_invalidate_iter, (void*)uc);
|
|
} else {
|
|
uc_exit_invalidate_iter((gpointer)&uc->exits[uc->nested_level - 1], NULL, (gpointer)uc);
|
|
}
|
|
|
|
cpu->created = false;
|
|
}
|
|
|
|
void vm_start(struct uc_struct* uc)
|
|
{
|
|
resume_all_vcpus(uc);
|
|
}
|