1. 程式人生 > >Understand Qemu TCG

Understand Qemu TCG

  1. The whole executing process of QEMU

    main() //vl.c

    main_loop() //vl.c

    x86_cpu_realizefn() //例項化虛擬機器CPU裝置模型

    qemu_init_vcpu() //KVM沒有enable,並且啟用了TCG的情況下,tcg_enabled()

    qemu_tcg_init_vcpu()//啟動VCPU執行緒,執行緒處理函式為qemu_tcg_cpu_thread_fn

    qemu_tcg_cpu_thread_fn()

    tcg_exec_all()

    tcg_cpu_exec()

    cpu_exec() //cpu-exee.c

    tb = tb_find_fast(); // translate target code to host assemble  language and host machine code.

    next_tb = cpu_tb_exec()

    next_tb = tcg_qemu_tb_exec(tc_ptr); // execute the code contained in translation block.

    cpu_exec() 中執行如下:

    next_tb = 0; /* force lookup of first TB */

    for(;;) {

       process interrupt request;

tb_find_fast();

 tcg_qemu_tb_exec(tc_ptr);   


}

tb_find_fast() {

    get translation block from tb cache;

    if not found

    tb_find_slow();

}



tb_find_slow() {

get translation block from hash table;

    if not found

        tb_gen_code();//translate it now

}



//generate host code

tb_gen_code() // return a translated block

cpu_gen_code()

gen_intermediate_code(env, tb);

gen_intermediate_code_internal()

pc_ptr = disas_insn(dc, pc_ptr);

  gen_code_size = tcg_gen_code(s, gen_code_buf); //generate machine code

        tcg_gen_code_common(s, gen_code_buf, -1);



2. How do QEMU execute host instructions containing in translation block ?

2.1 There is a array defined in exec.c:

//uint8_t code_gen_prologue[1024] code_gen_section;(老版本)

struct TCGContext {

//……

uint8_t *code_ptr;

/* Code generation */

uint8_t *code_gen_prologue;

};


2.2 in function tcg_prologue_init() in tcg.c

/* init global prologue and epilogue */
s->code_buf = s->code_gen_prologue;
s->code_ptr = s->code_buf;

  tcg_target_qemu_prologue
(s);

then, {s->code_ptr) points to the address of code_gen_prologue.



2.3 function: tcg_target_qemu_prologue(), call tcg_out_push(), which fills value to *(s->code_ptr).

/* Generate global QEMU prologue and epilogue code */staticvoid tcg_target_qemu_prologue(TCGContext *s){int i, frame_size, push_size, stack_addend;/* TB prologue *//* Save all callee saved registers. */for(i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++){
        tcg_out_push
(s, tcg_target_callee_save_regs[i]);}/* Reserve some stack space. */
    push_size
= 1 + ARRAY_SIZE(tcg_target_callee_save_regs);
    push_size
*= TCG_TARGET_REG_BITS / 8;

    frame_size
= push_size + TCG_STATIC_CALL_ARGS_SIZE;
    frame_size
=(frame_size + TCG_TARGET_STACK_ALIGN - 1)&~(TCG_TARGET_STACK_ALIGN - 1);
    stack_addend
= frame_size - push_size;
    tcg_out_addi
(s, TCG_REG_ESP,-stack_addend);/* jmp *tb. */
    tcg_out_modrm
(s, OPC_GRP5, EXT5_JMPN_Ev, tcg_target_call_iarg_regs[0]);/* TB epilogue */
    tb_ret_addr
= s->code_ptr;

    tcg_out_addi
(s, TCG_REG_ESP, stack_addend);for(i = ARRAY_SIZE(tcg_target_callee_save_regs)- 1; i >= 0; i--){
        tcg_out_pop
(s, tcg_target_callee_save_regs[i]);}
    tcg_out_opc
(s, OPC_RET, 0, 0, 0);}


2.4 After getting a translation block by calling tb_find_fast(), call tcg_qemu_tb_exec(tc_ptr), which is a macro defined as following:

# define tcg_qemu_tb_exec(env, tb_ptr) \
((uintptr_t (*)(void *, void *))tcg_ctx.code_gen_prologue)(env, tb_ptr)


code_gen_prologue(tb_ptr) is casted to a function with two parameter, in such a way, execute host machine code stored in code_gen_prologue.



3. Distinction between user mode and system mode emulation of QEMU?

QEMU has two operating modes:

  • Full system emulation. In this mode, QEMU emulates a full system (for example a PC), including one or several processors and various peripherals. It can be used to launch different Operating Systems without rebooting the PC or to debug system code.
  • User mode emulation. In this mode, QEMU can launch processes compiled for one CPU on another CPU. It can be used to launch the Wine Windows API emulator (http://www.winehq.org) or to ease cross-compilation and cross-debugging.

4. One macro glue

#define xglue(x, y) x ## y
#define glue(x, y) xglue(x, y)

#define SUFFIX _mmx

void glue(helper_pcmpistrm, SUFFIX)(Reg *d, Reg *s,uint32_t ctrl)


First, this function is expanded to :(glue(x, y) -> xglue(x, y))

xglue(helper_pcmpistrm,_mmx)(Reg *d, Reg *s,uint32_t ctrl)


Second, (  xglue(x, y) -> x ## y )

helper_pcmpistrm_mmx(Reg *d, Reg *s,uint32_t ctrl)


So,

void glue(helper_pcmpistrm, SUFFIX)(Reg *d, Reg *s,uint32_t ctrl)

==

helper_pcmpistrm_mmx(Reg *d, Reg *s,uint32_t ctrl)


5. How do the qemu translate virtual address to physical address when load a instruction ?

phys_pc = get_page_addr_code(env, pc);

/* NOTE: this function can trigger an exception *//* NOTE2: the returned address is not exactly the physical address:    it is the offset relative to phys_ram_base */staticinline tb_page_addr_t get_page_addr_code(CPUState *env1, target_ulong addr){int mmu_idx, page_index, pd;void*p;

    page_index
=(addr >> TARGET_PAGE_BITS)&(CPU_TLB_SIZE - 1);
    mmu_idx
= cpu_mmu_index(env1);if(unlikely(env1->tlb_table[mmu_idx][page_index].addr_code !=(addr & TARGET_PAGE_MASK))){
        ldub_code
(addr);}
    pd
= env1->tlb_table[mmu_idx][page_index].addr_code &~TARGET_PAGE_MASK;if(pd > IO_MEM_ROM &&!(pd & IO_MEM_ROMD)){#if defined(TARGET_SPARC)|| defined(TARGET_MIPS)
        do_unassigned_access
(addr, 0, 1, 0, 4);#else
        cpu_abort
(env1,"Trying to execute code outside RAM or ROM at 0x" TARGET_FMT_lx "\n", addr);#endif}
    p
=(void*)(unsignedlong)addr
+ env1->tlb_table[mmu_idx][page_index].addend;return qemu_ram_addr_from_host(p);}


5. TCG .vs. Dyngen

6. Helper function

http://www.greensocs.com/Projects/QEMUSystemC/docs/QEMUSystemC/QEMUSystemCDataflow



7. Memory simulation in QEMU

http://www.slideshare.net/zchen/memory-simulation-in-qemu



8. For system simulation.

b = ldub_code(s->pc);

will call this function(in softmmu_header.h):

glue is a marico, after replacing, it is :

     static inline RES_TYPE ld_{USUFFIX}_{MEMSUFFIX}(target_ulong ptr);

staticinline RES_TYPE glue(glue(ld, USUFFIX), MEMSUFFIX)(target_ulong ptr){int page_index;
    RES_TYPE res
;
    target_ulong addr
;unsignedlong physaddr;int mmu_idx;

    addr
= ptr;
    page_index
=(addr >> TARGET_PAGE_BITS)&(CPU_TLB_SIZE - 1);
    mmu_idx
= CPU_MMU_INDEX;if(unlikely(env->tlb_table[mmu_idx][page_index].ADDR_READ !=(addr &(TARGET_PAGE_MASK |(DATA_SIZE - 1))))){
        res
= glue(glue(__ld, SUFFIX), MMUSUFFIX)(addr, mmu_idx);}else{
        physaddr
= addr + env->tlb_table[mmu_idx][page_index].addend;
        res
= glue(glue(ld, USUFFIX), _raw)((uint8_t*)physaddr);}return res;}


9. QEMU device model