QEMU中TCG翻譯流程

聲明:本文使用的qemu源碼版本爲qemu-3.1.0-rc0

前言:qemu中採用事件驅動架構和並行架構相結合的方式來工作的。qemu中的線程主要有Vcpu線程,main_loop線程、I/O線程和workthread線程,其中main_loop屬於主線程。

1. 翻譯流程總體框架

2. 具體流程

  1> 在vl.c的main函數中創建單板machine

​
current_machine = MACHINE(object_new(object_class_get_name(OBJECT_CLASS(machine_class))));

2>  在啓動單板的過程中選擇了單板的參數如:

​
./qemu-system-ppc -M mac99

此時會調用mac99單板對應的模型,其建模過程對應qemu源碼中的hw\ppc\mac_newworld.c文件

3> 初始化CPU(mac_newworld.c中調用)

​
core99_machine_class_init->(mc->init=ppc_core99_init)

  mac_newworld.c函數中ppc_core_init函數創建CPU具體過程如下:

​
for (i = 0; i < smp_cpus; i++) {
        cpu = POWERPC_CPU(cpu_create(machine->cpu_type));
        env = &cpu->env;

        /* Set time-base frequency to 100 Mhz */
        cpu_ppc_tb_init(env, TBFREQ);
        qemu_register_reset(ppc_core99_reset, cpu);
    }

 其中其中 cpu = POWERPC_CPU(cpu_create(machine->cpu_type))實現對於CPU的創建,其函數調用具體如下:(該函數原型位於cpu.c文件中)

​
CPUState *cpu_create(const char *typename)
{
    Error *err = NULL;
    CPUState *cpu = CPU(object_new(typename));
    object_property_set_bool(OBJECT(cpu), true, "realized", &err);
    if (err != NULL) {
        error_report_err(err);
        object_unref(OBJECT(cpu));
        exit(EXIT_FAILURE);
    }
    return cpu;
}

其中object_new函數會通過調用target/ppc/traslate_init.inc.c文件中的函數來創建CPU

4> 在創建CPU時每一個Vcpu都會創建一個vcpu線程(這就要和TCG翻譯有關了!!!)

​
ppc_cpu_class_init->ppc_cpu_realize->qemu_init_vcpu

5> 在cpus.c文件中qemu_init_vcpu函數的定義,該函數調用了qemu_tcg_init_vcpu函數

void qemu_init_vcpu(CPUState *cpu)
{
    cpu->nr_cores = smp_cores;
    cpu->nr_threads = smp_threads;
    cpu->stopped = true;

    if (!cpu->as) {
        /* If the target cpu hasn't set up any address spaces itself,
         * give it the default one.
         */
        cpu->num_ases = 1;
        cpu_address_space_init(cpu, 0, "cpu-memory", cpu->memory);
    }

    if (kvm_enabled()) {
        qemu_kvm_start_vcpu(cpu);
    } else if (hax_enabled()) {
        qemu_hax_start_vcpu(cpu);
    } else if (hvf_enabled()) {
        qemu_hvf_start_vcpu(cpu);
    } else if (tcg_enabled()) {
        qemu_tcg_init_vcpu(cpu);
    } else if (whpx_enabled()) {
        qemu_whpx_start_vcpu(cpu);
    } else {
        qemu_dummy_start_vcpu(cpu);
    }

    while (!cpu->created) {
        qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
    }
}

6> qemu_tcg_init_vcpu函數的定義

static void qemu_tcg_init_vcpu(CPUState *cpu)
{
    char thread_name[VCPU_THREAD_NAME_SIZE];
    static QemuCond *single_tcg_halt_cond;
    static QemuThread *single_tcg_cpu_thread;
    static int tcg_region_inited;

    assert(tcg_enabled());
    /*
     * 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).
     */
    if (!tcg_region_inited) {
        tcg_region_inited = 1;
        tcg_region_init();
    }

    if (qemu_tcg_mttcg_enabled() || !single_tcg_cpu_thread) {
        cpu->thread = g_malloc0(sizeof(QemuThread));
        cpu->halt_cond = g_malloc0(sizeof(QemuCond));
        qemu_cond_init(cpu->halt_cond);

        if (qemu_tcg_mttcg_enabled()) {
            /* create a thread per vCPU with TCG (MTTCG) */
            parallel_cpus = true;
            snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG",
                 cpu->cpu_index);

            qemu_thread_create(cpu->thread, thread_name, qemu_tcg_cpu_thread_fn,
                               cpu, QEMU_THREAD_JOINABLE);

        } else {
            /* share a single thread for all cpus with TCG */
            snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "ALL CPUs/TCG");
            qemu_thread_create(cpu->thread, thread_name,
                               qemu_tcg_rr_cpu_thread_fn,
                               cpu, QEMU_THREAD_JOINABLE);

            single_tcg_halt_cond = cpu->halt_cond;
            single_tcg_cpu_thread = cpu->thread;
        }
#ifdef _WIN32
        cpu->hThread = qemu_thread_get_handle(cpu->thread);
#endif
    } else {
        /* For non-MTTCG cases we share the thread */
        cpu->thread = single_tcg_cpu_thread;
        cpu->halt_cond = single_tcg_halt_cond;
        cpu->thread_id = first_cpu->thread_id;
        cpu->can_do_io = 1;
        cpu->created = true;
    }
}

該函數中調用(這裏就創建了一個vcpu的線程,在執行客戶機代碼時進行翻譯)

qemu_thread_create(cpu->thread, thread_name, qemu_tcg_cpu_thread_fn,cpu, QEMU_THREAD_JOINABLE);

 接下來其函數調用關係如下:

                                               

 7> 接下來重點解析cpu_exec()函數(該函數位於accel\tcg\cpu-exec.c文件下)

 在line724會調用 tb = tb_find(cpu, last_tb, tb_exit, cflags)用來查找TB(translation block)(這裏的TB是已經翻譯爲host   code 的代碼)

 查找到TB之後會調用cpu_loop_exec_tb(cpu, tb, &last_tb, &tb_exit)函數來執行翻譯好的主機代碼。

 8> tb_find函數解析

static inline TranslationBlock *tb_find(CPUState *cpu,
                                        TranslationBlock *last_tb,
                                        int tb_exit, uint32_t cf_mask)
{
    TranslationBlock *tb;
    target_ulong cs_base, pc;
    uint32_t flags;

    tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
    if (tb == NULL) {
        mmap_lock();
        tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask);
        mmap_unlock();
        /* We add the TB in the virtual pc hash table for the fast lookup */
        atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb);
    }
#ifndef CONFIG_USER_ONLY
    /* We don't take care of direct jumps when address mapping changes in
     * system emulation. So it's not safe to make a direct jump to a TB
     * spanning two pages because the mapping for the second page can change.
     */
    if (tb->page_addr[1] != -1) {
        last_tb = NULL;
    }
#endif
    /* See if we can patch the calling TB. */
    if (last_tb) {
        tb_add_jump(last_tb, tb_exit, tb);
    }
    return tb;
}

該段代碼的主要功能是返回翻譯好的TB,首先調用tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask)函數查看該PC對應的代碼是否已經翻譯如果翻譯了,則直接返回TB如果沒有則需要調用tb_gen_code函數來進行翻譯。如果該基本塊不在cache中,則需要使用tb_gen_code函數進行翻譯並放到cache中。

其中 tb_lookup__cpu_state函數會調用cpu_get_tb_cpu_state獲取當前客戶機的pc寄存器中的值,接下來調用tb_jmp_cache_hash_func根據pc的值獲取存儲TB的hash表的索引,接下來調用atomic_rcu_read(&cpu->tb_jmp_cache[hash])函數來獲取cache中存儲的TB。tb_htable_lookup函數中通過調用get_page_addr_code函數獲取客戶機操作系統的物理內存地址(phys_pc)。

9> tb_hen_code 目標機代碼翻譯爲主機代碼的過程

    phys_pc = get_page_addr_code(env, pc)  //獲取當前要翻譯的pc對應的物理地址

    tb = tb_alloc(pc)    //爲該pc分配新的TB與之對應

    gen_intermediate_code //調用該函數將target code轉換爲TCG操作碼

     tcg_gen_code   //使用該函數將TCG操作碼轉換爲host code

 

發佈了32 篇原創文章 · 獲贊 5 · 訪問量 2604
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章