1. 程式人生 > >qemu tcg程式碼執行流程

qemu tcg程式碼執行流程

一.qemu簡介

         qemu是使用動態二進位制翻譯的cpu模擬器,它支援兩種執行模式:全系統模擬和使用者態模擬。在全系統模擬下,qemu可以模擬處理器和各種外設,可以執行作業系統。使用者態可以執行為另外一種cpu編譯的程序,前提是兩者執行的os要一致。qemu使用了動態二進位制翻譯將targetinstruction翻譯成hostinstruction,完成這個工作的是tcg模組。為了移植性和通用性方面的考慮,qemu定義了mirco-op,首先qemu會將targetinstruction翻譯成mirco-op,然後tcgmirco-op翻譯成host instruction

Qemu程式碼翻譯流程:target instruction ->micro-op->tcg->host instruction

2.qemu程式碼執行流程:

    1. 這部分主要是建立了一個為執行tcg翻譯和執行的執行緒,它的函式是qemu_tcg_cpu_thread_fn,這個函式會呼叫tcg_exec_all,最後cpu_exec.

main
cpu_init
qemu_init_vcpu
qemu_tcg_init_vcpu

qemu_tcg_cpu_thread_fn

     2.執行主函式(cpu_exec)

                主要是處理中斷異常,

 找到程式碼翻譯塊,然後執行.

  1. for(;;) 
  2. process interruptrequest; 
  3. tb_find_fast(); 
  4. tcg_qemu_tb_exec(tc_ptr); 

        qemu會將翻譯好到程式碼塊暫存起來,因此首先會去檢視該pc對應的程式碼是否已經翻譯,如果已經存在直接返回,否則就進入tb_find_slow,進行翻譯。

  1. 139 staticinline TranslationBlock *tb_find_fast(CPUArchState *env) 
  2. 140 
  3. 141     TranslationBlock *tb; 
  4. 142     target_ulong cs_base, pc; 
  5. 143     int flags; 
  6. 144 
  7. 145 
  8. 148     cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); 
  9. 149     tb env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)]; 
  10. 150     if (unlikely(!tb || tb->pc != pc || tb->cs_base != cs_base || 
  11. 151                  tb->flags != flags)) 
  12. 152         tb tb_find_slow(env, pc, cs_base, flags); 
  13. 153     
  14. 154     return tb; 
  15. 155 

       進入tb_find_slow後會呼叫tb_gen_code,首先分配TranslationBlock描述符,將要翻譯的pc等資訊記錄下來,然後呼叫cpu_gen_code,這個函式完成程式碼翻譯工作。qemu將翻譯好的程式碼存在一個緩衝區裡面。

  1. 1029 TranslationBlock *tb_gen_code(CPUArchState *env, 
  2. 1030                               target_ulong pc, target_ulong cs_base, 
  3. 1031                               int flags, int cflags) 
  4. 1032 
  5. 1033     TranslationBlock *tb; 
  6. 1034     uint8_t *tc_ptr; 
  7. 1035     tb_page_addr_t phys_pc, phys_page2; 
  8. 1036     target_ulong virt_page2; 
  9. 1037     int code_gen_size; 
  10. 1038 
  11. 1039     phys_pc get_page_addr_code(env, pc); 
  12. 1040     tb tb_alloc(pc); 
  13. 1041     if (!tb) 
  14. 1042 
  15. 1043         tb_flush(env); 
  16. 1044 
  1. 1045         tb tb_alloc(pc); 
  2. 1046 
  3. 1047         tb_invalidated_flag 1; 
  4. 1048     
  5. 1049     tc_ptr code_gen_ptr; 
  6. 1050     tb->tc_ptr tc_ptr; 
  7. 1051     tb->cs_base cs_base; 
  8. 1052     tb->flags flags; 
  9. 1053     tb->cflags cflags; 
  10. 1054     cpu_gen_code(env, tb, &code_gen_size); 
  11. 1055     code_gen_ptr (void *)(((uintptr_t)code_gen_ptr code_gen_size 
  12. 1056                              CODE_GEN_ALIGN 1) ~(CODE_GEN_ALIGN 1)); 
  13. 1057 
  14. 1058 
  15. 1059     virt_page2 (pc tb->size 1) TARGET_PAGE_MASK; 
  16. 1060     phys_page2 -1; 
  17. 1061     if ((pc TARGET_PAGE_MASK) != virt_page2) 
  18. 1062         phys_page2 get_page_addr_code(env, virt_page2); 
  19. 1063     
  20. 1064     tb_link_page(tb, phys_pc, phys_page2); 
  21. 1065     return tb; 
  22. 1066 

     cpu_gen_code裡面首先是將targetinstruction翻譯成micro-op,然後將mirco-op翻譯成host機器碼。

  1. 54 int cpu_gen_code(CPUArchState *env, TranslationBlock *tb, int *gen_code_size_ptr) 
  2.  55 
  3.  56     TCGContext *s &tcg_ctx; 
  4.  57     uint8_t *gen_code_buf; 
  5.  58     int gen_code_size; 
  6.  59 #ifdef CONFIG_PROFILER 
  7.  60     int64_t ti; 
  8.  61 #endif 
  9.  62 
  10.  63 #ifdef CONFIG_PROFILER 
  11.  64     s->tb_count1++; 
  12.  66     ti profile_getclock(); 
  13.  67 #endif 
  14.  68     tcg_func_start(s); 
  15.  69 
  16.  70     gen_intermediate_code(env, tb); 
  17.  71 
  18.  72 
  19.  73     gen_code_buf tb->tc_ptr; 
  20.  74     tb->tb_next_offset[0] 0xffff; 
  21.  75     tb->tb_next_offset[1] 0xffff; 
  22.  76     s->tb_next_offset tb->tb_next_offset; 
  23.  77 #ifdef USE_DIRECT_JUMP 
  24.  78     s->tb_jmp_offset tb->tb_jmp_offset; 
  25.  79     s->tb_next NULL; 
  26.  80 #else
  27.  81     s->tb_jmp_offset NULL; 
  28.  82     s->tb_next tb->tb_next; 
  29.  83 #endif 
  30.  84 
  31.  85 #ifdef CONFIG_PROFILER 
  32.  86     s->tb_count++; 
  33.  87     s->interm_time += profile_getclock() ti; 
  34.  88     s->code_time -= profile_getclock(); 
  35.  89 #endif 
  36.  90     gen_code_size tcg_gen_code(s, gen_code_buf); 
  37.  91     *gen_code_size_ptr gen_code_size; 
  38.  92 #ifdef CONFIG_PROFILER 
  39.  93     s->code_time += profile_getclock(); 
  40.  94     s->code_in_len += tb->size; 
  41.  95     s->code_out_len += gen_code_size; 
  42.  96 #endif 
  43.  97 
  44.  98 #ifdef DEBUG_DISAS 
  45.  99     if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM)) 
  46. 100         qemu_log("OUT: [size=%d]\n"*gen_code_size_ptr); 
  47. 101         log_disas(tb->tc_ptr, *gen_code_size_ptr); 
  48. 102         qemu_log("\n"); 
  49. 103         qemu_log_flush(); 
  50. 104     
  51. 105 #endif 
  52. 106     return 0; 
  53. 107 

       qemutarget翻譯成中間碼時,將操作碼和運算元分開儲存,分別存在gen_opc_bufgen_opparam_buf中。翻譯的過程就是不斷向gen_opc_bufgen_opparam_buf中填充操作碼和運算元。接下來就是tcg_gen_code.

  1. 2175 int tcg_gen_code(TCGContext *s, uint8_t *gen_code_buf) 
  2. 2176 
  3. 2177 #ifdef CONFIG_PROFILER 
  4. 2178     
  5. 2179         int n; 
  6. 2180         (gen_opc_ptr gen_opc_buf); 
  7. 2181         s->op_count += n; 
  8. 2182         if (n s->op_count_max) 
  9. 2183             s->op_count_max n; 
  10. 2184 
  11. 2185         s->temp_count += s->nb_temps; 
  12. 2186         if (s->nb_temps s->temp_count_max) 
  13. 2187             s->temp_count_max s->nb_temps; 
  14. 2188     
  15. 2189 #endif 
  16. 2190 
  17. 2191     tcg_gen_code_common(s, gen_code_buf, -1); 
  18. 2192 
  19. 2193 
  20. 2194     flush_icache_range((tcg_target_ulong)gen_code_buf, 
  21. 2195                        (tcg_target_ulong)s->code_ptr); 
  22. 2196 
  23. 2197     return s->code_ptr  gen_code_buf; 
  24. 2198 

      tcg_gen_code的工作是將中間碼翻譯成host機器碼,它的主要函式是tcg_gen_code_common.

  1. 2045 staticinlineint tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf, 
  2. 2046                                       long search_pc) 
  3. 2047 
  4. 2048     TCGOpcode opc; 
  5. 2049     int op_index; 
  6. 2050     const TCGOpDef *def; 
  7. 2051     unsigned int dead_args; 
  8. 2052     const TCGArg *args; 
  9. 2053 
  10. 2054 #ifdef DEBUG_DISAS 
  11. 2055     if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) 
  12. 2056         qemu_log("OP:\n"); 
  13. 2057         tcg_dump_ops(s); 
  14. 2058         qemu_log("\n"); 
  15. 2059     
  16. 2060 #endif 
  17. 2061 
  18. 2062 #ifdef USE_TCG_OPTIMIZATIONS 
  19. 2063     gen_opparam_ptr 
  20. 2064         tcg_optimize(s, gen_opc_ptr, gen_opparam_buf, tcg_op_defs); 
  21. 2065 #endif 
  22. 2066 
  23. 2067 #ifdef CONFIG_PROFILER 
  24. 2068     s->la_time -= profile_getclock(); 
  25. 2069 #endif 
  26. 2070     tcg_liveness_analysis(s); 
  27. 2071 #ifdef CONFIG_PROFILER 
  28. 2072     s->la_time += profile_getclock(); 
  29. 2073 #endif 
  30. 2074 
  31. 2075 #ifdef DEBUG_DISAS 
  32. 2076     if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP_OPT))) 
  33. 2077         qemu_log("OP after liveness analysis:\n"); 
  34. 2078         tcg_dump_ops(s); 
  35. 2079         qemu_log("\n"); 
  36. 2080     
  37. 2081 #endif 
  38. 2082 
  39. 2083     tcg_reg_alloc_start(s); 
  40. 2084 
  41. 2085     s->code_buf gen_code_buf; 
  42. 2086     s->code_ptr gen_code_buf; 
  43. 2087 
  44. 2088     args gen_opparam_buf; 
  45.  2089     op_index 0; 
  46. 2090 
  47. 2091     for(;;) 
  48. 2092         opc gen_opc_buf[op_index]; 
  49. 2093 #ifdef CONFIG_PROFILER 
  50. 2094         tcg_table_op_count[opc]++; 
  51. 2095 #endif 
  52. 2096         def &tcg_op_defs[opc]; 
  53. 2097 #if 
  54. 2098         printf("%s: %d %d %d\n"def->name, 
  55. 2099                def->nb_oargs, def->nb_iargs, def->nb_cargs); 
  56. 2100         //        dump_regs(s);
  57. 2101 #endif 
  58. 2102         switch(opc) 
  59. 2103         case INDEX_op_mov_i32: 
  60. 2104 #if TCG_TARGET_REG_BITS == 64 
  61. 2105         case INDEX_op_mov_i64: 
  62. 2106 #endif 
  63. 2107             dead_args s->op_dead_args[op_index]; 
  64. 2108             tcg_reg_alloc_mov(s, def, args, dead_args); 
  65. 2109             break
  66. 2110         case INDEX_op_movi_i32: 
  67. 2111 #if TCG_TARGET_REG_BITS == 64 
  68. 2112         case INDEX_op_movi_i64: 
  69. 2113 #endif 
  70. 2114             tcg_reg_alloc_movi(s, args); 
  71. 2115             break
  72. 2116         case INDEX_op_debug_insn_start: 
  73. 2117 
  74. 2118             break
  75. 2119         case INDEX_op_nop: 
  76. 2120         case INDEX_op_nop1: 
  77. 2121         case INDEX_op_nop2: 
  78. 2122         case INDEX_op_nop3: 
  79. 2123             break
  80. 2124         case INDEX_op_nopn: 
  81. 2125             args += args[0]; 
  82. 2126             goto next; 
  83. 2127         case INDEX_op_discard: 
  84. 2128             
  85.  2129                 TCGTemp *ts; 
  86. 2130                 ts &s->temps[args[0]]; 
  87. 2131 
  88. 2132                 if (!ts->fixed_reg) 
  89. 2133                     if (ts->val_type == TEMP_VAL_REG) 
  90. 2134                         s->reg_to_temp[ts->reg] -1; 
  91. 2135                     ts->val_type TEMP_VAL_DEAD; 
  92. 2136                 
  93. 2137             
  94. 2138             break
  95. 2139         case INDEX_op_set_label: 
  96. 2140             tcg_reg_alloc_bb_end(s, s->reserved_regs); 
  97. 2141             tcg_out_label(s, args[0], s->code_ptr); 
  98. 2142             break
  99. 2143         case INDEX_op_call: 
  100. 2144             dead_args s->op_dead_args[op_index]; 
  101. 2145             args += tcg_reg_alloc_call(s, def, opc, args, dead_args); 
  102. 2146             goto next; 
  103. 2147         case INDEX_op_end: 
  104. 2148             goto the_end; 
  105. 2149         default
  106. 2150 
  107. 2151             if (def->flags TCG_OPF_NOT_PRESENT) 
  108. 2152                 tcg_abort(); 
  109. 2153             
  110. 2154 
  111. 2157             dead_args s->op_dead_args[op_index]; 
  112. 2158             tcg_reg_alloc_op(s, def, opc, args, dead_args); 
  113. 2159             break
  114. 2160         
  115. 2161         args += def->nb_args; 
  116. 2162     next: 
  117. 2163         if (search_pc >= && search_pc s->code_ptr gen_code_buf) 
  118. 2164             return op_index; 
  119. 2165         
  120. 2166         op_index++; 
  121. 2167 #ifndef NDEBUG 
  122. 2168         check_regs(s); 
  123. 2169 #endif 
  124. 2170     
  125.  2171  the_end: 
  126. 2172     return -1; 
  127. 2173 

       大部分執行default分支,tcg_reg_alloc_op主要是分析該指令的輸入、輸出約束,根據這些約束分配暫存器等,然後呼叫tcg_out_op將該中間碼翻譯成host機器碼。

3.翻譯程式碼塊的執行

     程式碼塊翻譯好之後,主函式呼叫tcg_qemu_tb_exec,該函式會進入tcg的入口函式。

hostarm為例,入口函式主要是儲存暫存器的狀態,然後將tcg_qemu_tb_exec傳進來的第一的引數(env)TCG_AREG0,然後跳轉至tb_ptr,開始執行程式碼。同時代碼執行的返回地址也確定了,返回後恢復之前儲存的狀態。

以hostarm為例,入口函式主要是儲存暫存器的狀態,然後將tcg_qemu_tb_exec傳進來的第一的引數(env)TCG_AREG0,然後跳轉至tb_ptr,開始執行程式碼。同時代碼執行的返回地址也確定了,返回後恢復之前儲存的狀態。
  1. 100 #define tcg_qemu_tb_exec(env, tb_ptr) 
  2. 101     ((long __attribute__ ((longcall)) 
  3. 102       (*)(void *, void *))code_gen_prologue)(env, tb_ptr) 

      code_gen_prologuetcg的入口函式,在tcg初始化的時候會生成相應的程式碼.

        hostarm為例,入口函式主要是儲存暫存器的狀態,然後將tcg_qemu_tb_exec傳進來的第一的引數(env)TCG_AREG0,然後跳轉至tb_ptr,開始執行程式碼。同時代碼執行的返回地址也確定了,返回後恢復之前儲存的狀態。

  1. 1881 staticvoid tcg_target_qemu_prologue(TCGContext *s) 
  2. 1882 
  3. 1883 
  4. 1886 
  5. 1887 
  6. 1888     tcg_out32(s, (COND_AL << 28) 0x092d5ff0); 
  7. 1889 
  8. 1890     tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); 
  9. 1891 
  10. 1892     tcg_out_bx(s, COND_AL, tcg_target_call_iarg_regs[1]); 
  11. 1893     tb_ret_addr s->code_ptr; 
  12. 1894 
  13. 1895 
  14. 1896     tcg_out32(s, (COND_AL << 28) 0x08bd9ff0); 
  15. 1897